[BZOJ 2818] Gcd
Link:https://www.lydsy.com/JudgeOnline/problem.php?id=2818
Algorithm:
一道比较水的数论题
可以发现只要找到所有素数,对于每个素数计算出1~n/p的互质的数的个数
一看到互质,联想到欧拉函数
预处理欧拉函数的前缀和,结果为sigma(pre[n/prime[i]]*2-1) (有序数对,舍去(1,1)特解)
Code:
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int MAXN=1e7+10; bool mark[MAXN]; int n,prime[MAXN],phi[MAXN],cnt=0; ll pre[MAXN],res=0; void Getphi() { phi[1]=1; //线性筛 for(int i=2;i<=n;i++) { if(!mark[i]) prime[++cnt]=i,phi[i]=i-1; for(int j=1;j<=cnt;j++) { if(prime[j]*i>n) break; mark[prime[j]*i]=true; if(i%prime[j]==0){phi[i*prime[j]]=prime[j]*phi[i];break;} //保证复杂度的关键 else phi[i*prime[j]]=phi[i]*phi[prime[j]]; } } } int main() { cin >> n; Getphi(); for(int i=1;i<=n;i++) pre[i]=pre[i-1]+phi[i]; for(int i=1;i<=cnt;i++) res+=pre[n/prime[i]]*2-1; cout << res; return 0; }
Review:
1、互质数对数 -----> 欧拉函数
2、线性筛
其中i%prime[j]=0时break是保证复杂度的关键
这样才能保证每个合数都由自己最小的质因数更新 ( 证明:https://wenku.baidu.com/view/2d706761aa00b52acec7ca63.html )
同时线性筛和素数筛在筛的顺序上有差异,线性筛是先广度,素数筛是先深度(原因在于phi[i]要先求出)