GCD

洛咕

题意:给定整数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\n",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\n",ans);
    return 0;
}

posted on 2019-05-18 07:57  PPXppx  阅读(109)  评论(0编辑  收藏  举报