首页 > 代码库 > HDU1695:GCD(容斥原理+欧拉函数+质因数分解)好题
HDU1695:GCD(容斥原理+欧拉函数+质因数分解)好题
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1695
题目解析:
Given 5 integers: a, b, c, d, k, you‘re to find x in a...b, y in c...d that GCD(x, y) = k.
题目又说a==c==1,所以就是求[1,b]与[1,d]中gcd等于k的个数,因为若gcd(x,y)==z,那么gcd(x/z,y/z)==1,又因为不是z的倍数的肯定不是,所以不是z的倍数的可以直接去掉,所以只要将b和d除以k,然后就转化成了求两个范围中互质的对数了。即求[1,b/k],与[1,d/k]中互质的数目,让b<d,又因为 (x=5, y=7) and (x=7, y=5) are considered to be the same.
所以先求[1,b/k]中互质的数目,即phi[1]+phi[2]+phi[3].....+phi[b/k](其中phi[i]为i的欧拉函数值),再从区间[b/k+1,d/k]枚举与区间[1,b/k]中互质的数目。其中求与区间[1,b/k]中互质的数目可以通过容斥原理求得与区间[1,b/k]中不互质的数目,相减便可以求得结果。这题折腾了一中午,一直TLE,代码在后面贴了,之后看大神的博客知道了哪里超时的原因,每个数的质因子可以在打表求欧拉函数的时候顺便求出来,一种哈希的思想,这样就不用在枚举的时候每一个数在求一遍他的质因子了,好题!
代码如下:(608ms)
#include <iostream>#include <stdio.h>#include <string.h>#include <algorithm>#include <queue>#include <math.h>using namespace std;typedef __int64 ll;ll sum,phi[100010];int cnt[100010][11],f[100010],a,b,c,d,x;void init(){ memset(f,0,sizeof(f)); for(int i=1; i<=100000; i++) { phi[i]=0; f[i]=0; } phi[1]=1; for(int i=2; i<=100000; i++) { if(!phi[i]) { for(ll j=i; j<=100000; j+=i) { if(!phi[j]) phi[j]=j; phi[j]=phi[j]/i*(i-1); cnt[j][f[j]++]=i;//算是哈希吧,很精辟啊,这种写法要学习 } } phi[i]+=phi[i-1]; }}ll gcd(ll A,ll B){ return B==0?A:gcd(B,A%B);}void dfs(ll now,ll num,ll lcm,ll &coun,int key){ lcm=cnt[key][now]/gcd(cnt[key][now],lcm)*lcm; if(num&1) { coun+=b/lcm; } else { coun-=b/lcm; } for(ll i=now+1; i<f[key]; i++) dfs(i,num+1,lcm,coun,key);}int main(){ int T,K=0; init(); scanf("%d",&T); while(T--) { scanf("%d%d%d%d%d",&a,&b,&c,&d,&x); if(x == 0 || x > b || x > d) { printf("Case %d: 0\n",++K); continue; } b/=x; d/=x; sum=0; if(b>d) swap(b,d); sum+=phi[b]; for(int i=b+1; i<=d; i++) { ll coun=0; for(int j=0; j<f[i]; j++) { dfs(j,1,cnt[i][j],coun,i); } sum+=(b-coun); } printf("Case %d: %I64d\n",++K,sum); } return 0;}
TLE的:(TLE了一中午 3000ms++)
#include <iostream>#include <stdio.h>#include <string.h>#include <algorithm>#include <queue>#include <math.h>using namespace std;typedef __int64 ll;ll a,b,c,d,x,sum,top,cnt[100010],we;//********int phi[100010],su[100010],prime[100010];void init(){ we=0; prime[we++]=2; su[0]=su[1]=0; su[2]=1; for(int i=3; i<100005; i++) if(i%2==0) su[i]=0; else su[i]=1; double t=sqrt(100005*1.0); for(ll i=3; i<=t; i++) { if(su[i]) { for(ll j=i*i; j<100005; j=j+i) { su[j]=0; } } } for(ll i=3; i<=100003; i++) { if(su[i]) prime[we++]=i; } memset(phi,0,sizeof(phi)); phi[1]=1; for(ll i=2; i<=100003; i++) { if(!phi[i]) { for(ll j=i; j<=100003; j+=i) { if(!phi[j]) phi[j]=j; phi[j]=phi[j]/i*(i-1); } } }}ll gcd(ll A,ll B){ return B==0?A:gcd(B,A%B);}void dfs(ll now,ll num,ll lcm,ll &coun){ lcm=cnt[now]/gcd(cnt[now],lcm)*lcm; if(num&1) { coun+=b/lcm; //printf("hsum======%I64d\n",sum); } else { coun-=b/lcm; } for(ll i=now+1; i<top; i++) dfs(i,num+1,lcm,coun);}void cal(ll key,ll &coun){ top=0; ll KK=0; for(ll i=prime[0]; i<=key; i=prime[++KK]) { if(key%i==0) { cnt[top++]=i; key/=i; while(key%i==0) key/=i; } } if(key!=1) { cnt[top++]=key; } for(ll i=0; i<top; i++) { dfs(i,1,cnt[i],coun); }}int main(){ int T,K=0; init(); scanf("%d",&T); while(T--) { scanf("%I64d%I64d%I64d%I64d%I64d",&a,&b,&c,&d,&x); if(x == 0 || x > b || x > d) { printf("Case %d: 0\n",++K); continue; } b/=x; d/=x; sum=0; if(b>d) swap(b,d); //cout<<"b=="<<b<<" "<<"d=="<<d<<endl; for(int i=1; i<=b; i++) { sum+=phi[i]; } //cout<<"sumsss=="<<sum<<endl; for(ll i=b+1; i<=d; i++) { if(su[i]) { sum+=b; continue; } ll coun=0; cal(i,coun); sum+=(b-coun); } printf("Case %d: %I64d\n",++K,sum); } return 0;}
HDU1695:GCD(容斥原理+欧拉函数+质因数分解)好题