AcWing 220 最大公约数

题目

AcWing 220

给定整数 \(N\) ,求 \(1\leq x,y\leq N\leq 10^7\)\(\gcd(x,y)\) 为素数的 \((x,y)\) 有多少对

分析

用朴素的算法求解显然复杂度为 \(O(N^2\log^2 N)\) ,所以考虑变形:

\[\begin{aligned} &\sum_{x=1}^N\sum_{y=1}^N[\gcd(x,y)\in\mathbb{P}]\\ =&\sum_{k\in\mathbb{P}}\sum_{x_1=1}^{\lfloor\frac{N}{k}\rfloor}\sum_{y_1=1}^{\lfloor\frac{N}{k}\rfloor}[x_1\perp y_1]\label{XX} \end{aligned} \]

观察后两个和式,可以发现下面这个与它形式相近的式子能转化为欧拉函数的形式:

\[\sum_{x_1=1}^{\lfloor\frac{N}{k}\rfloor}\sum_{y_1=1}^{x_1}[x_1\perp y_1]=\sum_{x_1=1}^{\lfloor\frac{N}{k}\rfloor}\varphi(x_1) \]

将上式乘 \(2\) ,可以发现它与最开始的式子相比,多加了 \(x_1=y_1\)\([x_1\perp y_1]\) 的部分,此时 \(x_1=y_1=1\) ,设集合 \(P\) 表示小于等于 \(N\) 的素数,那么多加的次数就等于 \(|P|\) ,所以原式可以化为:

\[\begin{aligned} &2\sum_{k\in P}\sum_{x_1=1}^{\lfloor\frac{N}{k}\rfloor}\varphi(x_1)-|P|\\ =&2\sum_{k\in P}sum(\lfloor\frac{N}{k}\rfloor)-|P| \end{aligned} \]

其中 \(sum(x)\) 表示欧拉函数的 \(x\) 项前缀和

那么我们可以用线性筛以 \(O(N)\) 同时筛出素数和欧拉函数,并以 \(O(N)\) 计算前缀和,所以时间复杂度为 \(O(N)\)

代码

#include<bits/stdc++.h>
using namespace std;

const int MAX_N = 10000000 + 5;
int n, cnt = 0;
bool is_prime[MAX_N];
int prime[MAX_N], phi[MAX_N];
long long sum[MAX_N];

void sieve(int n)
{
    memset(is_prime, 1, sizeof(is_prime));
    cnt = 0;
    is_prime[1] = false;
    phi[1] = 1;
    for(int i = 2; i <= n; i++) {
        if(is_prime[i]) {
            prime[++cnt] = i;
            phi[i] = i - 1;
        }
        for(int j = 1; j <= cnt && prime[j] * i <= n; j++) {
            int t = prime[j] * i;
            is_prime[t] = false;
            if(i % prime[j]) {
                phi[t] = phi[i] * phi[prime[j]];
            } else {
                phi[t] = phi[i] * prime[j];
                break;
            }
        }
    }
}

int main()
{
    cin >> n;
    sieve(n);
    long long ans = 0;
    sum[0] = 0;
    for(int i = 1; i <= n; i++)
        sum[i] = sum[i - 1] + phi[i];
    for(int i = 1; i <= cnt; i++)
        ans += 2ll * sum[n / prime[i]];
    ans -= cnt;
    cout << ans << endl;
    return 0;
}
posted @ 2022-02-24 23:57  f(k(t))  阅读(17)  评论(0编辑  收藏  举报