zoukankan      html  css  js  c++  java
  • 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
    ",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;
    }
    
    
  • 相关阅读:
    【51NOD 1478】括号序列的最长合法子段
    【BZOJ 3527】【ZJOI 2014】力
    【BZOJ 2194】快速傅立叶之二
    【CodeVS 3123】高精度练习之超大整数乘法 &【BZOJ 2197】FFT快速傅立叶
    【BZOJ 2693】jzptab
    【BZOJ 2154】Crash的数字表格
    【BZOJ 3529】【SDOI 2014】数表
    【BZOJ 2820】YY的GCD
    【BZOJ 2301】【HAOI 2011】Problem b
    【POJ 3294】Life Forms 不小于k个字符串中的最长子串
  • 原文地址:https://www.cnblogs.com/PPXppx/p/10884525.html
Copyright © 2011-2022 走看看