题意:给定整数N,求(1<=x,y<=N)且(gcd(x,y))为素数的数对((x,y))有多少对?
分析:以为是赤裸裸的双倍经验,就直接把那道题代码kuai过来,结果超时了,两道题的时限不一样,但是开(O_2)还是能过的.先把辣鸡代码放一下.
#include<bits/stdc++.h>
#define LL long long
using namespace std;
inline int read(){
int s=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){s=s*10+ch-'0';ch=getchar();}
return s*w;
}
const int N=10000005;
int v[N],sum[N],prime[N],mu[N];
inline void get_mu(){
mu[1]=1;int m=0;
for(int i=2;i<N;i++){
if(!v[i]){
v[i]=i;
prime[++m]=i;
mu[i]=-1;
}
for(int j=1;j<=m;j++){
if(i*prime[j]>N)break;
v[i*prime[j]]=prime[j];
if(i%prime[j]==0)break;
mu[i*prime[j]]=-mu[i];
}
}
for(int i=1;i<=m;i++)
for(int j=1;j*prime[i]<N;j++)
sum[j*prime[i]]+=mu[j];
for(int i=1;i<N;i++)sum[i]+=sum[i-1];
}
int main(){
get_mu();
int n=read();LL ans=0;
for(int l=1,r;l<=n;l=r+1){
r=n/(n/l);
ans+=1ll*(n/l)*(n/l)*(sum[r]-sum[l-1]);
}
printf("%lld
",ans);
return 0;
}
接着进入正题,注意到本题与YY的GCD那道题的区别是本题没有(N,M)之分,(x,y)的范围都是(1)~(N),这不就是便于我们枚举么......枚举一个质数p,对于二元组((i,j))其中一个枚举项i,对答案产生贡献的必然是1-n间与之gcd为p的数的个数,根据莫比乌斯反演的套路转化一下就是[n/p]这个范围内与i互质的数的个数,也就是欧拉函数(phi(i)),所以一个质数p对答案产生的贡献为(sum_{i=1}^{[n/p]}phi[i]*2-1)(乘2是因为统计的是二元组,减去1是因为gcd(1,1)会重复统计),故维护出欧拉函数的前缀和,直接枚举质数累加进答案即可.
#include<bits/stdc++.h>
#define LL long long
using namespace std;
inline int read(){
int s=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){s=s*10+ch-'0';ch=getchar();}
return s*w;
}
const int N=10000005;
int n,m,v[N],prime[N];
long long phi[N];
inline void get_phi(int n){
phi[1]=1;
for(int i=2;i<=n;i++){
if(!v[i]){
v[i]=i;
prime[++m]=i;
phi[i]=i-1;
}
for(int j=1;j<=m;j++){
if(i*prime[j]>n)break;
v[i*prime[j]]=prime[j];
if(i%prime[j]==0){
phi[i*prime[j]]=phi[i]*prime[j];
break;
}
phi[i*prime[j]]=phi[i]*(prime[j]-1);
}
}
for(int i=1;i<=n;i++)phi[i]+=phi[i-1];
}
int main(){
n=read();get_phi(n);LL ans=0;
for(int i=1;i<=m;i++)
ans+=phi[n/prime[i]]*2-1;
printf("%lld
",ans);
return 0;
}