HDU-4135 Co-prime 容斥定理

该题就是给定一个数,求某个区间内有多少数与这个数互质。

首先我们要明确,对于一个给定的区间,我们如果能够求出这个区间内所有与其不互质的数的个数的话,那么互质数的个数也就求出来。这个叫做反面求解。

对于任何一个正整数N,都有其唯一的素因子分解形式,我们可以这样想,任何与N不互质的数一定是其某一个质因子的倍数,所以我们通过某一个质因子能够确定一个范围内的有多少个数该质因子的倍数的数。但是这并不是所有不互质数的充分条件,对于另外的质因子同样满足能够确定一系列的数,我们要做的就是融过容斥定理求他们的并集。

代码如下:

#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;

typedef long long int Int64;

Int64 a, b, num, rec[6000000], lim, ans;

int idx;

Int64 GCD(Int64 a, Int64 b)
{
    Int64 t;
    while (b) {
        t = a;
        a = b;
        b = t % b;
    }
    return a;
}

Int64 LCM(Int64 a, Int64 b)
{
    return a / GCD(a, b) * b;
}

void dfs(int p, Int64 co, int sign)
{
    if (p > idx) {
        return;
    }
    if (p > 0) {
        ans += sign * (b / co - (a-1) /co);
    }
    for (int i = p + 1; i <= idx; ++i) {
        dfs(i, LCM(co, rec[i]), -sign);
    }
}

int main()
{
    int T, ca = 0;
    scanf("%d", &T);
    while (T--) {
        idx = 0;
        ans = 0;
        scanf("%I64d %I64d %I64d", &a, &b, &num);
        if (num == 1) {
            printf("Case #%d: %I64d\n", ++ca, b-a+1);
            continue;
        }
        lim = (int)sqrt(double(num));
        if (num % 2 == 0) {
            while (num % 2 == 0) {
                num >>= 1;
            }
            rec[++idx] = 2;
        }
        for (int i = 3;  i <= lim; ++i) {
            if (num % i == 0) {
                while (num % i == 0) {
                    num /= i;
                }
                rec[++idx] = i;
            }
        }
        
        if (num != 1) {
            rec[++idx] = num;
        }
        
        dfs(0, 1, -1);
        printf("Case #%d: %I64d\n", ++ca, b - a + 1 - ans);
    }
    return 0;
}
posted @ 2012-07-28 10:07  沐阳  阅读(540)  评论(0编辑  收藏  举报