1. 程式人生 > >GCD HDU - 1695 (容斥原理 + 尤拉函式)

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;
}