HDU-1695 GCD 容斥定理

这题首先要将等式两边除以k,这样在求两边的互质数的个数就是最后的结果了。我们采用的策略就是用小区间的每一个数去匹配大区间的数。但是如果每次都去分解一个数的质因子的话,那么会TLE,因此先预处理出1-100000每个数的质因子再进行计算。

代码如下:

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

int a, b, c, d, k, rec[300000], idx;

int start[100005], end[100005], S, E;

long long int ret;

void cut(int x)
{
    int LIM = (int)sqrt(double(x));
    if (x % 2 == 0) {
        rec[++idx] = 2;
        while (x % 2 == 0) { x /= 2; }
    }
    for (int i = 3; i <= LIM; i += 2) {
        if (x % i == 0) {
            rec[++idx] = i;
            while (x % i == 0) { x /= i; }
        }
    }
    if (x != 1) { rec[++idx] = x; }
}

void pre()
{
    idx = 0;
    for (int i = 1; i <= 100000; ++i) {
        start[i] = idx;
        cut(i);
        end[i] = idx;
    }
}

void dfs(int p, int num, int sign, int &sum, int l, int r)
{
    if (p == E) {
        if (num > 1) {
            sum += sign * (r / num - (l-1) / num);
        }
        return;
    }
    dfs(p+1, num, sign, sum, l, r);
    dfs(p+1, num*rec[p+1], -sign, sum, l, r);
}

int main()
{
    int T, ca = 0, sum, l, r;
    pre();
    scanf("%d", &T);
    while (T--) {
        ret = 0;
        scanf("%d %d %d %d %d", &a, &b, &c, &d, &k);
        printf( "Case %d: ", ++ca);
        if (!k) {
            puts("0");
            continue;
        }
        b /= k, d /= k;
        if (b > d) {
            int t = b;
            b = d;
            d = t;
        }
        for (int i = 1; i <= b; ++i) {
            S = start[i], E = end[i];
            sum = 0;
            l = i, r = d;
            dfs(S, 1, -1, sum, l, r);
            ret += (r-l+1) - sum;
        }
        printf("%I64d\n", ret);
    }
    return 0;
}
posted @ 2012-07-29 11:09  沐阳  阅读(285)  评论(0编辑  收藏  举报