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;
}