HDU 1695 GCD

题目链接
测试提交

题意:\(x\sqsubseteq [1,b]\),\(y\sqsubseteq [1,d]\) ,求\(gcd(x,y)=k\)\((x,y)\)的个数

分析:显然,等价于求\(b,d\)同时除以\(k\),求\(gcd(x',y')=1\)的个数,因为\((1,3)\)\((3,1)\)是一种情况,所以要限制\((x<y)\)

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;

//返回1-m中与n互素的数的个数
vector<LL> p;
LL cal(LL n, LL m) {
    p.clear();
    for (int i = 2; i * i <= n; i++) {
        if (n % i == 0) {
            p.push_back(i);
            while (n % i == 0) n /= i;
        }
    }
    if (n > 1) p.push_back(n); //求n的素因子

    LL s = 0;           // 1到m中与n不互素的数的个数

    //枚举子集,不能有空集,所以从1开始
    for (LL i = 1; i < 1 << p.size(); i++) { //从1枚举到(2^素因子个数)
        LL cnt = 0;
        LL t = 1;
        for (LL j = 0; j < p.size(); j++) { //枚举每个素因子
            if (i & (1 << j)) {        //有第i个因子
                cnt++;                 //计数
                t *= p[j];             //乘上这个质因子
            }
        }
        //容斥原理
        if (cnt & 1) //选取个数为奇数,加
            s += m / t;
        else //选取个数为偶数,减
            s -= m / t;
    }
    return m - s; //返回1-m中与n互素的数的个数
}

int main() {
    int T, ca = 0;
    scanf("%d", &T);
    while (T--) {
        LL a, b, c, d, k;
        scanf("%lld%lld%lld%lld%lld", &a, &b, &c, &d, &k);
        // k=0时需要特判,因为我们想要 x'=x/k ,y'=y/k,不能随意随,需要判断
        if (!k) {
            printf("Case %d: 0\n", ++ca);
            continue;
        }
        b /= k, d /= k;
        //因为 (1,3)与 (3,1)算1个,所以要限制x<y
        if (b > d) swap(d, b);

        LL ans = 0;
        for (LL i = 1; i <= d; i++)   //只算小区间
            ans += cal(i, min(i, b)); //求区间[1,b]内与i互质的个数

        printf("Case %d: %lld\n", ++ca, ans);
    }
    return 0;
}
posted @ 2022-06-20 09:37  糖豆爸爸  阅读(19)  评论(0编辑  收藏  举报
Live2D