题目链接: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 ",++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 ",++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 ",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 ",++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 ",++K,sum); } return 0; }