P2568 GCD
题意描述:
给你一个 \(n\) 求 \(\displaystyle\sum_{i=1}^{n}\sum_{j=1}^{n} [gcd(i,j)为质数]\)
数据范围:\(n\leq 10^7\)
sloution
首先我们很容易想出来怎么打暴力,我们可以对于每个 \(i\) 和 \(j\) 都求一遍他们的 \(gcd\) 最后累计一下 \(gcd\) 为质数的就完成了。
可是这样得神仙时间复杂度 为 \(O(n^2)\), 对于这道题,我们肯定会 TLE。
我们就只能苦逼的想正解QAQ
然后就开始我们的推柿子时间QAQ
我们考虑这个柿子: \(gcd(x,y) = p\)
那么这个柿子可以写成 \(gcd(x' * p , y' * p) = p\)
提出 \(p\) 来就可以变成 \(gcd(x',y') = 1; x' = x / p ,y' = y / p\);
我们有一个推论就是:
\(\displaystyle\sum_{i=1}^{n}\sum_{j=1}^{n} [gcd(i,j) = 1]\) 不就相当于 \(\displaystyle 2 \times \sum_{i=1}^{N} \varphi(i) -1\)
证明:
因为 \(gcd(i,j) = gcd(j,i)\), 所以 \(\displaystyle\sum_{i=1}^{n}\sum_{j=1}^{n} [gcd(i,j) == 1] = (2\times \sum_{i=1}^{n}\sum_{j=1}^{i} [gcd(i,j) == 1])-1\), (\(-1\) 是因为 \(i = j = 1\) 的时候算了两次)。
\(\displaystyle\sum_{j=1}^{i} [gcd(i,j) == 1]\) 等价于 \(1-i\) 内与 \(i\) 互质的个数,也就是 \(\varphi(i)\)
那么我们的式子可以写成: \(2\times \displaystyle\sum_{i=1}^{n} \varphi(i) - 1\)
因此对于每个 \(p\),他的贡献就是 \(\displaystyle 2\times \sum_{i=1}^{N\over p} \varphi(i) -1\).
那么有多少个这样的 \(p\) 呢???
其实 \(p\) 就是 \(N\) 以内的质数,对于每个 \(p\),求一下他的贡献,在相加,不就可以轻松AC了吗QAQ。。。
那么最后的总柿子就是
\(\displaystyle\sum_{p} (2\times \sum_{i=1}^{N\over p} \varphi(i))-1 ( p 是1~n的质数)\)
优化: 我们再求 \(p\) 的贡献时,不必要一个个的去求欧拉函数的值,我们可以考虑维护
一下前缀和。这样就会减少不少时间了...
最后附上我的代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 1e7+10;
long long n,ans = 0,cnt;
long long prime[N] ,phi[N], tot[N];
bool check[N];
void calc(int n){//边进行欧拉筛,边求欧拉函数
memset(check,-1,sizeof(check));
phi[1] = 1;
for(int i = 2; i <= n; i++){
if(check[i]){
phi[i] = i-1;
prime[++cnt] = i;
}
for(int j = 1; j <= cnt; j++){
if(i * prime[j] > n) break;
check[i*prime[j]] = 0;
if(i % prime[j] == 0){
phi[i*prime[j]] = phi[i] * prime[j];
break;
}
else{/积性函数性质
phi[i*prime[j]] = phi[i] * phi[prime[j]];
}
}
}
for(int i = 1; i <= n; i++) tot[i] = tot[i-1] + phi[i];//前缀和
}
int main(){
scanf("%d",&n);
calc(n);
for(int i = 1; i <= cnt; i++){//枚举每个p
ans += 2 * tot[n/prime[i]] - 1;//求p的贡献
}
printf("%d\n",ans)
return 0;
}