hdu-1695 GCD---容斥定理

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=1695

题目大意:

求解区间[1, n]和[1, m]中有多少对不同的x和y使得gcd(x, y) == k

其中x=5 y=7和x=7 y=5是同一对

解题思路:

首先如果gcd为k说明[1, n]中只有k的倍数为x,同理在[1, m]中也只有k的倍数为y。

所以如果先特判,k=0或者k>n或者k>m都是不存在解的情况。

之后n /= k, m /= k,这是之选出k的倍数,作为x和y,并且gcd(x, y) = k,就是等价于求在现在的1-n区间和1-m区间中求互质对数。

还需考虑重复的情况,所以枚举m的时候求区间[m, n]与m互质的数,这样不会重复枚举。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 const int maxn = 1e6 + 10;
 5 ll a[50], tot;
 6 ll gcd(ll a, ll b)
 7 {
 8     return b == 0 ? a : gcd(b, a % b);
 9 }
10 void init(ll n)//求出n的素因子
11 {
12     tot = 0;
13     for(ll i = 2; i * i <= n; i++)
14     {
15         if(n % i == 0)
16         {
17             a[tot++] = i;
18             while(n % i == 0)n /= i;
19         }
20     }
21     if(n != 1)a[tot++] = n;
22 }
23 ll sum(ll m)//求[1, m]中与n互质的个数
24 {
25     ll ans = 0;
26     for(int i = 1; i < (1 << tot); i++)//a数组的子集
27     {
28         ll num = 0;
29         for(int j = i; j; j >>= 1)if(j & 1)num++;//统计i的二进制中1的个数
30         ll lcm = 1;
31         for(int j = 0; j < tot; j++)
32             if((1 << j) & i)
33         {
34             lcm = lcm / gcd(lcm, a[j]) * a[j];
35             if(lcm > m)break;
36         }
37         if(num & 1)ans += m / lcm;//奇数加上
38         else ans -= m / lcm;//偶数减去
39     }
40     return m - ans;
41 }
42 int main()
43 {
44     int T, cases = 0, a, b, n, m, k;
45     cin >> T;
46     while(T--)
47     {
48         ll ans;
49         scanf("%d%d%d%d%d", &a, &n, &b, &m, &k);
50         if(k == 0 || n < k || m < k)ans = 0;
51         else
52         {
53             n /= k, m /= k;
54             if(n < m)swap(n, m);
55             ans = n;
56             for(int i = 2; i <= m; i++)
57             {
58                 init(i);
59                 //cout<<ans<<endl;
60                 ans += sum(n) - sum(i - 1);
61             }
62         }
63         cout<<"Case "<<++cases<<": "<<ans<<endl;
64     }
65     return 0;
66 }

 

posted @ 2018-05-24 23:22  _努力努力再努力x  阅读(259)  评论(0编辑  收藏  举报