Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1323 Accepted Submission(s): 449
2 1 3 1 5 1 1 11014 1 14409 9
Case 1: 9 Case 2: 736427HintFor the first sample input, all the 9 pairs of numbers are (1, 1), (1, 2), (1, 3), (1, 4), (1, 5), (2, 3), (2, 5), (3, 4), (3, 5).思路:题目意思不难已知给定k,x,y求 1<=a<=x 1<=b<=y 中满足 gcd(a,b)=k 的(a,b)对数。(注意数对是无序的)。 1<=x,y<=10w, 0<=k<=10w
题目有比较恶心的一点,数据有k==0的,这时显然答案是0,没有2个数的gcd为0。
首先,gcd是没啥用的。因为约掉gcd后两个数互质。于是我们可以让x/=k y/=k并且假设 x<=y
然后题目变成了 2个数分别在区间[1..x]和[1..y]中的互质数有多少对。
大体思路:
枚举[1..y]中每个数i 判断[1..min(x,i)]中有多少数与i互质,统计个数。(注意,枚举的是比较大的区间[1..y])。
显然如果i是质数,则[1..min(x,i)]中与i互质的个数是全体的个数或者i-1个。(取决于x和i的大小)。
当i不是质数时,i分解质因数后,质因数的次数不影响结果。我们看另外那个区间有多少个和i不互质(减一下就好了),于是我们只要看另外那个区间中有多少个数是i质因数的倍数就好了。
区间[1..w]中 p的倍数 显然有 w/p个。
我们枚举i的质因数利用容斥原理:
看另外那个区间有多少个数与i不互质。
容斥原理的具体如下:
区间中与i不互质的个数 = (区间中i的每个质因数的倍数个数)-(区间中i的每两个质因数乘积的倍数)+(区间中i的每3个质因数的成绩的倍数个数)-(区间中i的每4个质因数的乘积)+...
于是问题变成了统计每个数的不同质因数的个数而忽略次数。这个可以用筛法。具体做法如下:
对每个数保存一个真质因数的列表。初始每个列表的长度为0。然后从2开始,分别检查每个数的列表长度,如果列表长度不为0,则这个数是合数,跳过;如果这个长度为0,则我们找到了一个质数,同时再把这个数的倍数(不包含本身)的列表里加入这个数。
这样筛一次下来,我们保存了每个数的真质因数列表,问题得到解决,还要注意结果用要用__int64。
#include <iostream> using namespace std; #define N 100005 #define ll __int64 ll eule[N]; int p[N][15]; int num[N]; void init() { int i,j; eule[1]=1; for(i=2;i<N;i++)//筛选法得到数的素因子及每个数的欧拉函数值 { if(!eule[i]) { for(j=i;j<N;j+=i) { if(!eule[j]) eule[j]=j; eule[j]=eule[j]*(i-1)/i; p[j][num[j]++]=i; } } eule[i]+=eule[i-1]; } } ll DFS(int x,int MAX,int q) //求MAX内与q不互质的个数 { int i; ll res=0; for (i=x;i<num[q];i++) { res+=MAX/p[q][i]-DFS(i+1,MAX/p[q][i],q); } return res; } int main() { int t,a,b,MAX,MIN,k,flag,i; __int64 res; init(); scanf("%d",&t); flag=0; while(t--) { flag++; scanf("%d%d%d%d%d",&a,&a,&b,&b,&k); if(k==0) { printf("Case %d: 0/n",flag); continue; } MAX=a>b?a:b; MIN=a>b?b:a; MAX/=k; MIN/=k; res=eule[MIN]; for(i=MIN+1;i<=MAX;i++) { res+=MIN-DFS(0,MIN,i); } printf("Case %d: %I64d/n",flag,res); } return 0; }