HDU 1695 GCD (欧拉函数+容斥原理)

GCD

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 6446    Accepted Submission(s): 2356


Problem Description
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).
 

题意: 在1~a, 1~b中挑出(x,y)满足gcd(x,y) = k , 求(x,y) 的对数 , a,b<=10^5

 

思路:x与y只有公因子k,则说明gcd(x/k,y/k)=1,我们令a/k>=b/k  ,对于[1,a/k]中的i,如果i<=b/k,便 是求1~i-1中与i互质的个数,即求i的欧拉函数值。

对于i>a/k的情况,只有将i质因子分解,然后容斥原理了。(这题好像还可以用莫比乌斯反演下次写)

区间中与i不互质的个数 = (区间中i的每个质因数的倍数个数)-(区间中i的每两个质因数乘积的倍数)+(区间中i的每3个质因数的乘积的倍数个数)-(区间中i的每4个质因数的乘积)+...

 

知识了解(by 百科):

欧拉函数

在数论,对正整数n,欧拉函数是少于或等于n的数中与n互质的数的数目。

φ函数的值  通式:φ(x)=x(1-1/p1)(1-1/p2)(1-1/p3)(1-1/p4)…..(1-1/pn),其中p1, p2……pn为x的所有质因数,x是不为0的整数。φ(1)=1(唯一和1互质的数(小于等于1)就是1本身)。 (注意:每种质因数只一个。比如12=2*2*3那么φ(12)=12*(1-1/2)*(1-1/3)=4。

若n是质数p的k次幂,φ(n)=p^k-p^(k-1)=(p-1)p^(k-1),因为除了p的倍数外,其他数都跟n互质。

欧拉函数是积性函数——若m,n互质,φ(mn)=φ(m)φ(n)。特殊性质:当n为奇数时,φ(2n)=φ(n), 证明与上述类似。

容斥原理

在计数时,必须注意无一重复,无一遗漏。为了使重叠部分不被重复计算,人们研究出一种新的计数方法,这种方法的基本思想是:先不考虑重叠的情况,把包含于某内容中的所有对象的数目先计算出来,然后再把计数时重复计算的数目排斥出去,使得计算的结果既无遗漏又无重复,这种计数的方法称为容斥原理

 

 

也可表示为
设S为有限集,
,则
两个集合的容斥关系公式:A∪B = A+B - A∩B (∩:重合的部分)
三个集合的容斥关系公式:A∪B∪C = A+B+C - A∩B - B∩C - C∩A +A∩B∩C
 
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

#define LL long long
const int MAXN = 100005;
int euler[MAXN+5];
void getEuler()
{
    memset(euler,0,sizeof(euler));
    euler[1] = 1;
    for(int i = 2; i <= MAXN; i++)
        if(!euler[i])
            for(int j = i; j <= MAXN; j += i)
            {
                if(!euler[j])
                    euler[j] = j;
                euler[j] = euler[j]/i*(i-1);
            }
}
int prime[MAXN+1];
void getPrime()
{
    memset(prime,0,sizeof(prime));
    for(int i=2; i<=MAXN; i++)
    {
        if(!prime[i])prime[++prime[0]]=i;
        for(int j=1; j<=prime[0]&&prime[j]<=MAXN/i; j++)
        {
            prime[prime[j]*i]=1;
            if(i%prime[j]==0break;
        }
    }
}
long long factor[100][2];//factor[i][0]素因子,factor[i][1]素因子的个数
int fatCnt;
int getFactors(long long x)
{
    fatCnt=0;
    long long tmp=x;
    for(int i=1; prime[i]<=tmp/prime[i]; i++)
    {
        factor[fatCnt][1]=0;
        if(tmp%prime[i]==0)
        {
            factor[fatCnt][0]=prime[i];
            while(tmp%prime[i]==0)
            {
                factor[fatCnt][1]++;
                tmp/=prime[i];
            }
            fatCnt++;
        }
    }
    if(tmp!=1)
    {
        factor[fatCnt][0]=tmp;
        factor[fatCnt++][1]=1;
    }
    return fatCnt;
}
int cal(int a,int b)//求1~b中与a互质的个数
{
    getFactors(a);
    int ans = 0;
    int maxn = 1 << fatCnt;
    for(int i=1; i<maxn; i++)
    {
        int tmp = 1;
        int cnt = 0;
        for(int j=0; j<fatCnt; j++)
        {
            if(i & (1 << j))
            {
                cnt++;
                tmp *= factor[j][0];
            }
        }
        if(cnt & 1)//这里是欧拉函数的运用,奇数个就加上,偶数减去
            ans += b/tmp;
        else
            ans -= b/tmp;
    }
    return b - ans;
}
int main()
{
    //freopen("in.txt","r",stdin);
    int t,a,b,c,d,k;
    getPrime();
    getEuler();
    cin >> t;
    for(int tt = 1; tt<=t; tt++)
    {
        cin >> a >> b >> c >> d >> k;
        printf("Case %d: ",tt);
        if(k == 0)
        {
            printf("0\n");
            continue;
        }
        a = b / k;
        b = d / k;
        if(a < b)
            swap(a,b);
        LL ans = 0;
        for(int i=1; i<=b; i++)
            ans += euler[i];
        for(int i=b+1; i<=a; i++)
        {
            ans += cal(i,b);
           // printf("i = %d , ans = %I64d\n",i,ans);
        }
        printf("%I64d\n",ans);
    }
    return 0;
}
View Code
posted @ 2015-03-04 15:37  Doli  阅读(198)  评论(0编辑  收藏  举报