转载请注明出处,谢谢 http://blog.csdn.net/ACM_cxlove?viewmode=contents by---cxlove
很NB的数论题啊。
求1~a中的任意x,和1~b中任意y能组成gcd(x,y)=k的对数,二元组是无序的。
首先想到暴力,1-a中k的倍数的数与1-b中k的倍数的数,如果gcd(x,y)=k,则计数,显然肯定行不通。
x与y只有公因子k,则说明gcd(x/k,y/k)=1,貌似有点进展,求1~a/k,和1~b/k中的互质的对数。
枚举1~a/k中的任意一个数i,求1~b/k中与其互质的个数然后叠加?想法是对的,由于题目要求不重复的,即要避免(x,y)(y,x)这样的情况
回到问题求[1,a/k],[1,b/k]中的互质对数,我们令a/k>=b/k ,对于[1,a/k]中的i,如果i<=b/k,便 是求1~i-1中与i互质的个数,即求i的欧拉函数值。
对于i>a/k的情况,只有将i质因子分解,然后容斥原理了,参考了别人的写法,发现以前写的容斥弱爆了,不过发现以前自己写的好理解。
欧拉函数和,即法雷级数?会溢出,小心 WA
/* ID:cxlove PROB:hdu 1695 GCD DATA:2012.4.6 HINT:欧拉函数+容斥原理 */ #include<iostream> #include<cstdio> #include<cstring> #define N 100005 #define LL long long using namespace std; LL eular[N]; int prime[N][10],num[N]; void Prime(){ eular[1]=1; for(int i=2;i<N;i++){ if(eular[i]==i){ eular[i]=i-1; for(int j=2;j*i<N;j++){ eular[i*j]=eular[i*j]*(i-1)/i; prime[i*j][num[i*j]++]=i; } } eular[i]+=eular[i-1]; } } void Init(){ for(int i=1;i<N;i++) eular[i]=i; memset(prime,0,sizeof(prime)); memset(num,0,sizeof(num)); Prime(); } LL dfs(int idx,int b,int now){ LL ret=0; for(int i=idx;i<num[now];i++) ret+=b/prime[now][i]-dfs(i+1,b/prime[now][i],now); return ret; } int main(){ Init(); int t,a,b,c,d,k,cas=1; scanf("%d",&t); while(t--){ scanf("%d%d%d%d%d",&a,&b,&c,&d,&k); printf("Case %d: ",cas++); if(k==0){ printf("0\n"); continue; } a=b/k;b=d/k; if(a<b) swap(a,b); LL ans=eular[b]; //1-b中互质个数和,即欧拉函数和 for(int i=b+1;i<=a;i++) //1-b中与a互质的个数 ans+=b-dfs(0,b,i); printf("%I64d\n",ans); } return 0; }