GCD HDU - 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. 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

2
1 3 1 5 1
1 11014 1 14409 9

Sample Output

Case 1: 9
Case 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).
        

Solution:

我们要求(x, y)的数目,且(x, y)满足x在[a, b], y在[c, d], 并且gcd(x, y) == k。题目保证a == c == 1,我们就可以转化为求[1, b / k]与[1, d / k]中互素的(x, y)的数目。我们为了保证不重复([7, 5]和[5, 7]算一种情况),就使y恒大于x。

大致分为两种情况:

我们假设b < d(即使不是这样,交换他们的值一下就是了), b = b / k;    d = d / k;

y <= b的时候:很显然,直接求欧拉函数的和就好了。

y > b && y <= d 的时候:我们需要求[1, b]中与y互素的数的个数,直接求不太好求,我们先找不与y互素的数,然后用b减去就好了,至于如何求不与y互质的数目,则用到了容斥原理,我们将y的质因子都求出来了,只需要找到能被y的质因子整除的数的数目(在1~d的范围中)就能得到结果了。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#define div aaa        //在这里宏定义是有用的,以div作为数组名会编译错误,懒得一一改正了,所以就宏定义一发。
using namespace std;

typedef long long ll;
const int maxn = 1e5 + 1;

vector < int > div[maxn + 10];
int eu[maxn + 10];

void Init(int n)            //求1~n的数的所有质因子
{
    for(int i = 1; i <= n; ++ i)    div[i].clear();
    for(int i = 1; i <= n; ++ i)
    {
        int m = i;
        for(int j = 2; j * j <= i; ++ j)
        {
            if(m % j == 0){
                div[i].push_back(j);
                while(m % j == 0)
                    m /= j;
            }
        }
        if(m > 1)
            div[i].push_back(m);
    }
}

void Eular()        //筛法求欧拉函数
{
    memset(eu, 0, sizeof(eu));
    eu[1] = 1;
    for(int i = 2; i <= maxn; ++ i)
    {
        if(!eu[i])
        {
            for(int j = i; j <= maxn; j += i)
            {
                if(!eu[j])  eu[j] = j;
                eu[j] = eu[j] / i * (i - 1);
            }
        }
    }
}

int calc(int n, int m)
{
    int ans = 0;
    int top = div[m].size();
    for(int i = 1; i < (1 << top); ++ i)     //二进制枚举因子出现的情况
    {
        int cnt = 0, temp = 1;
        for(int j = 0; j < top; ++ j)
        {
            if((1 << j) & i)
            {
                cnt++;
                temp *= div[m][j];
            }
        }
        if(cnt & 1)     ans += n / temp;    //奇数加
        else            ans -= n / temp;    //偶数减
    }
    return n - ans;
}

int main()
{
   int t, a, b, c, d, k;
   int Case = 0;
   cin >> t;
   Eular();
   while(t --)
   {
        cin >> a >> b >> c >> d >> k;
        printf("Case %d: ", ++Case);
        if(k == 0 || k > b || k > d)
        {
            printf("0\n");
            continue;
        }
        if(b > d)
            swap(b, d);
        b = b / k;
        d = d / k;
        ll ans = 0;
        Init(d + 2);
        for(int i = 1; i <= b; ++ i)
            ans += eu[i];
        for(int i = b + 1; i <= d; ++ i)
            ans += calc(b, i);
        printf("%lld\n", ans);
   }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/aqa2037299560/article/details/84770630