1. 程式人生 > >hdu 1695 (容斥原理)

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. 

題意:

在a~b,c~d這兩個區間內找x,y,使得GCD(x,y)==k,題目又說a==c==1(真的無語,那還要輸入a,b幹嘛)

解題思路:

因為GCD(x,y)==k,所以GCD(x/k,y/k)==1

這樣問題就轉變為了求x/k和y/k互質的對數了,所以區間就可以縮小為1~b/k和1~d/k,所求的就是這兩個區間裡面互質數的對數了。(感覺網上很多都沒把這一步講明白,都是直接除以k)

假設b<d,(不滿足則交換),然後分兩個部分1~b和b+1~d

在1~b中使用容斥定理,會出現(1,2)和(2,1)兩種情況,(所以要分成兩個區間),列舉1~b的每一個數,用容斥定理計算,ans最後除以2;(這裡也可以用尤拉函式計算,但是感覺程式碼量要大點,乾脆都用容斥定理)

在b+1~d中就不會出現重複對數了,因為b+1已經大於b了,所以也是列舉然後直接加上

最後注意一下特判k==0時即可

小結:這種題目,看懂題目然後整理好思路,模板一套,處理好介面和資料範圍,就不難了(雖然對我來說還是有點難...OvO...)下次試一下莫比烏斯反演來寫

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;

int fac[100000];
int div(int n)
{
    int cnt=0;
    for(int i=2;i*i<=n;i++)
    {
        if(n%i==0)
        {
            fac[cnt++]=i;
            while(n%i==0) n/=i;
        }
    }
    if(n>1) fac[cnt++]=n;
    return cnt;//素數因子的個數
}
int solve(int cnt,int n)//容斥原理
{
    int ans=0;
    for(int i=1;i< (1<<cnt) ; i++)
    {
        int ones=0,mult=1;
        for(int j=0;j<cnt;j++)
        {
            if(i & (1<<j))
            {
                ones++;
                mult*=fac[j];
            }
        }
        if(ones&1)//奇數加,偶數減
            ans+= n/mult;
        else
            ans-= n/mult;
    }
    return n-ans;
}
int main()
{
    int t,cs=1;
    scanf("%d",&t);
    while(t--)
    {
        int a,b,c,d,k;
        scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
        if(k==0) {printf("Case %d: 0\n",cs++);continue;}//k==0特判
        if(b>d) swap(b,d);
        d/=k,b/=k;
        ll ans=0;
        for(int i=1;i<=b;i++)//1~b區間
        {
            int cnt=div(i);
            ans+=solve(cnt,b);
        }
        ans=(ans+1)/2;
        for(int i=b+1;i<=d;i++)//b+1~d區間
        {
            int cnt=div(i);
            ans+=solve(cnt,b);
        }
       printf("Case %d: %lld\n",cs++,ans);
    }
    return 0;
}