HDU 1695 GCD(容斥原理、去重)

题目链接:
HDU 1695 GCD
题意:
x[a,b],y[c,d]gcd(x,y)=k(x,y)a=c=1
分析:
这道题之前有莫比乌斯反演做过,现在纯粹用容斥以及 [1,r] 中和 x 互质数个数也能做。

因为这是无序的,我们不妨设db,枚举 i[1,b]
extra 记录当 i[1,d] [1,d] 中和 i 互质的数字个数,那么其实这部分是多算了的,而且除了(1,1)都计算了两次。
ans 记录当 i(d,b] [1,d] 中和 i 互质的数字个数,这部分是没有多算了的。
所以最终的答案就是:

ans+extra+12=ans+extra2+1

只不过这样做要比用莫比乌斯做耗时多点,而且也需要预处理 105 以内每个数的质因数。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <climits>
#include <cmath>
#include <ctime>
#include <cassert>
#include <vector>
#define IOS ios_base::sync_with_stdio(0); cin.tie(0);
using namespace std;
typedef long long ll;
const int MAX_N = 100010;

int vis[MAX_N];
vector <int> fac[MAX_N];

void GetFactor()
{
    memset(vis, 0, sizeof(vis));
    for(int i = 2; i < MAX_N; ++i) {
        if(vis[i] == 0) {
            fac[i].push_back(i);
            for(int j = i * 2; j < MAX_N; j += i) {
                fac[j].push_back(i);
                vis[j] = 1;
            }
        }
    }
}

inline int work(int r, int x) //[1, r]中和x互素的数个数
{
    if(x == 1) return r;
    int res = 0, size = fac[x].size();
    for(int i = 1; i < (1 << size); ++i) {
        int bits = 0, mul = 1;
        for(int j = 0; j < size; ++j) {
            if(i & (1 << j)) {
                bits++;
                mul *= fac[x][j];
            }
        }
        if(bits & 1) res += r / mul;
        else res -= r / mul;
    }
    return r - res;
}


int main()
{
    GetFactor();
    int T, cases = 0, a, b, c, d, k;
    scanf("%d", &T);
    while(T--) {
        scanf("%d%d%d%d%d", &a, &b, &c, &d, &k);
        if(k == 0) {
            printf("Case %d: 0\n", ++cases);
            continue;
        }
        b /= k, d /= k;
        if(b < d) swap(b, d);
        ll ans = 0, extra = 0, tmp;
        for(int i = 1; i <= b; ++i) {
            //printf("d = %d i = %d work(d, i) = %d\n", d, i, work(d, i));
            tmp = work(d, i);
            if(i <= d) extra += tmp;
            else ans += tmp;
        }
        //printf("extra = %lld\n", extra);
        printf("Case %d: %lld\n", ++cases, ans + (extra + 1)/ 2);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值