HDU1695 GCD【容斥原理】【歐拉函數】

題目鏈接:

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

 

題目大意:

給你5個整數a、b、c、d、k,在區間[a,b]中選一個數x,在區間[c,d]中選一個數y,使得x和y的

公約數爲k,即gcd(x,y) = k。現在問題來了:這樣的整數對共有多少對。

 

思路:

題目假定a = c = 1,那麼區間就變爲了[1,b]和[1,d]。求gcd(x,y) = k,其實可以將區間端點除以

k,得到[1,b/k]和[1,d/k]。問題就變爲了[1,b/k]和[1,d/k]中共有多少互素的整數對。

由於b可能大於d,爲了方便,我們令d > b,不滿足則交換d和b。

我們可以固定較小的區間[1,b/k], 用i遍歷另一個區間[1,d/k]。

當i <= b/k時,可以很容易看出,求i與[1,b/k]中互質的數的對數就是求i的歐拉函數,累加起來就是結果。

當i > b/k時,就變爲了和HDU4135、HDU2841類似的問題,用容斥定理來求。

 

AC代碼:

 

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define LL __int64
using namespace std;

LL Q[100010],factor[110000],num;

LL Prime[100010],phi[100010];
bool UnPrime[100010];

void Euler()
{
    int k = 0;
    phi[1] = 1;
    for(int i = 2; i <= 100000; ++i)
    {
        if(!UnPrime[i])
        {
            Prime[k++] = i;
            phi[i] = i-1;
        }
        for(int j = 0; j < k && Prime[j]*i <= 100000; ++j)
        {
            UnPrime[Prime[j]*i] = true;
            if(i % Prime[j] != 0)
            {
                phi[Prime[j]*i] = phi[i]*(Prime[j]-1);
            }
            else
            {
                phi[Prime[j]*i] = phi[i]*Prime[j];
                break;
            }
        }
    }
    for(int i = 2; i <= 100000; ++i)
        phi[i] += phi[i-1];
}

void Divid(LL n)
{
    num = 0;
    for(LL i = 2; i*i <= n; ++i)
    {
        if(n%i==0)
        {
            while(n%i==0)
            {
                n /= i;
            }
            factor[num++] = i;
        }
    }
    if(n != 1)
        factor[num++] = n;
}

LL solve(LL n)  //»¥³â¶¨Àí
{
    LL k,t,ans;
    t = ans = 0;
    Q[t++] = -1;
    for(LL i = 0; i < num; ++i)
    {
        k = t;
        for(LL j = 0; j < k; ++j)
            Q[t++] = -1*Q[j]*factor[i];
    }
    for(LL i = 1; i < t; ++i)
        ans += n/Q[i];
    return ans;
}

int main()
{
    int T,kase = 0;
    Euler();
    scanf("%d",&T);
    while(T--)
    {
        LL a,b,c,d,k,ans;
        scanf("%I64d %I64d %I64d %I64d %I64d",&a,&b,&c,&d,&k);
        if(k == 0)
        {
            printf("Case %d: 0\n",++kase);
            continue;
        }
        if(b > d)
            swap(b,d);
        b /= k;
        d /= k;
        ans = phi[b];
        for(LL i = b+1; i <= d; ++i)
        {
            Divid(i);
            ans += (b - solve(b));
        }

        printf("Case %d: %I64d\n",++kase,ans);
    }

    return 0;
}

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章