hdu 1695(欧拉函数+容斥原理)

来源:互联网 发布:国际象棋作弊软件 编辑:程序博客网 时间:2024/06/02 21:20

  GCD

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)


Problem Description
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. GCD(x, y) means the greatest common divisor of x and y. Since the number of choices may be very large, you're only required to output the total number of different number pairs.
Please notice that, (x=5, y=7) and (x=7, y=5) are considered to be the same.

Yoiu can assume that a = c = 1 in all test cases.
 

Input
The input consists of several test cases. The first line of the input is the number of the cases. There are no more than 3,000 cases.
Each case contains five integers: a, b, c, d, k, 0 < a <= b <= 100,000, 0 < c <= d <= 100,000, 0 <= k <= 100,000, as described above.
 

Output
For each test case, print the number of choices. Use the format in the example.
 

Sample Input
21 3 1 5 11 11014 1 14409 9
 

Sample Output
Case 1: 9Case 2: 736427
Hint
For 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).
 

大体思路:

     枚举[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,则我们找到了一个质数,同时再把这个数的倍数(不包含本身)的列表里加入这个数。

 


题目意思好懂,在[1...b]中选x,在[1....d]中选y,使gcd(x,y)=k,求不重复的对数

有一个小小的变形:在[1...b/k]中选x,在[1....d/k]中选y,使gcd(x,y)=k,求不重复的对数(因为x与y把k约去之后,肯定互为素数)

我们让d>=b;  然后在[1....d/k]进行枚举,对于每一个i,我们只要在1...min(i-1,b)中找到与i互质数,记录个数,然后累加就得到结果了

当i<=b/k时,我们可以直接用欧拉函数计算出与i互质的个数 (当然要先进行因子分解,才能求欧拉函数)

当b/k<i<=d/k时,就比较难求了,我们用b/k减去与i不互质的数的个数得到,求与i不互质的数的个数时就用到容斥原理,设i的素因子分别的p1,p2...pk,则1..b/k中p1的倍数组成集合A1,p2的倍数组成集合A2,p3到A3.....pk到Ak, 由于集合中会出现重复的元素, 所以用容斥原理来求A1并A2并A3.....并Ak的元素的数的个数.

remark:这里的容斥原理利用了dfs,我最开始的疑惑是在于代码里为什么没有体现出正负号,后面仔细分析了一下dfs的过程,确实是没错,因为递归进入到下一层后,同样是b'-a',那return到调用它的那一层(即上一层)时,同样也是拿b-a(a = b‘-a’) = b - b'+a‘,这个式子就体现出了正负号的变化。所以dfs算容斥原理,只需要把握好当前状态就OK了,后面交给递归去处理它。

这算是一道比较复杂的数论题了,参照了大牛门的代码....没办法自己没有能力想出来;

AC:

#include<iostream>#include<cstdio>#include<cstring>using namespace std;const int maxn = 100005;__int64 euler[maxn];   //存放每个数的欧拉函数值int num[maxn];   //存放当前数素数因子的个数int prime[maxn][10]; //存放当前数的素因子void EulerPrime()//筛选法求欧拉函数值{euler[1] = 1;for(int i = 2; i < maxn; i++){if(!euler[i]){for(int j = i; j < maxn; j += i){if(!euler[j])euler[j] = j;euler[j] = euler[j] / i * (i-1);prime[j][num[j]++] = i;}}euler[i] += euler[i-1];}}__int64 dfs(int cur,int b,int now){__int64 res = 0;for(int i = cur; i < num[now]; i++)res += b / prime[now][i] - dfs(i+1,b/prime[now][i],now);return res;}int main(){int t,cas = 1;cin>>t;EulerPrime();while(t--){int a,b,c,d,k;cin>>a>>b>>c>>d>>k;if(k==0)          {              printf("Case %d: 0\n",cas++);              continue;          }  if(b > d)swap(b,d);b /= k; d /= k;__int64 ans = euler[b];for(int i = b+1; i <= d; i++)ans += b - dfs(0,b,i);cout<<"Case "<<cas++<<": "<<ans<<endl;}return 0;}


0 0