您的位置:首页 > 其它

[BZOJ 2818] Gcd 线性筛+欧拉函数前缀和

2017-10-01 15:47 211 查看
题目传送门:【BZOJ 2818】

题目大意:输入整数 N,求 1 ≤ x , y ≤ N 且 gcd ( x , y ) 为质数的数对 ( x , y ) 的数目。其中 1 ≤ N ≤ 107。

题目分析:

由题,欲求 gcd ( x , y ) = p(1 ≤ x , y ≤ N,p 为质数),根据 gcd 的性质,

我们可将其转化一下,变成:求 gcd ( x , y ) = 1(1 ≤ x , y ≤ ⌊Np⌋)。

令 ⌊Np⌋ = d,设此时的答案为 anspi;在 1 ≤ x , y ≤ ⌊Np⌋ 这个范围内,根据欧拉函数 φ 的定义(与这个数互质的数的数量),可知:ansd = 2 * ( φ(d) + φ(d-1) + φ(d-2) + …… + φ(2) + φ(1) ) - 1。

对于上式,由于在数对 ( x , y ) 中,x 和 y 可以交换,因此答案要乘以 2;又因为对于 ( 1 , 1 ) 只有一种情况,所以最后的答案要减 1。

之后,我们对每个不大于 N 的质数进行类似的处理,最后得到的答案 ans = ∑anspi(所有的 pi 不大于 N)。

因此,我们只需要预处理出 107以内的所有质数,所有数的 φ 值,以及 φ 值的前缀和,枚举每个质数 pi 并求出对应的 anspi,最后求和即可(前缀和&最后的答案要开 long long)。

下面附上代码:

[cpp] view plain copy print?#include<cstdio>
const int MX=10000005;

int n,ptot=0,prime[MX],phi[MX]; //ptot:质数总数
bool isnot[MX];
long long ans=0,sum_phi[MX]; //sum_phi: φ的前缀和

void sieve(int n){
isnot[1]=true;
phi[1]=1;
for (register int i=2;i<=n;i++){
if (!isnot[i]){
prime[++ptot]=i;
phi[i]=i-1;
}
for (int t=1;t<=ptot;t++){
int j=prime[t]*i;
if (j>n) break;
isnot[j]=true;
phi[j]=phi[prime[t]]*phi[i];
if (i%prime[t]==0){
phi[j]=prime[t]*phi[i];
break;
}
}
}
}

int main(){
scanf(”%d”,&n);
sieve(n);
for (register int i=1;i<=n;i++) //预处理 φ的前缀和
sum_phi[i]=sum_phi[i-1]+phi[i];

for (register int i=1;i<=ptot;i++){ //枚举每个质数 pi并求出此时的 ans
int d=n/prime[i];
ans+=sum_phi[d]*2-1;
}
printf(”%lld”,ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: