HDU1695 GCD [容斥]

G C D GCD GCD


D e s c r i p t i o n \mathcal{Description} Description
给 出 n 、 m 、 k , 求 出 1 < = x < = n , 1 < = y < = m 且 g c d ( x , y ) = = k 的 ( x , y ) 的 对 数 给出n、m、k ,求出1<=x<=n, 1<=y<=m 且gcd(x,y) == k 的(x,y)的对数 nmk1<=x<=n,1<=y<=mgcd(x,y)==kx,y.

n , m , k < = 1 0 5 n,m,k<=10^5 n,m,k<=105


最 初 想 法 最初想法
按照 能量采集 的方法.
时间复杂度 O ( N l o g N ) O(NlogN) O(NlogN)

#include<cstdio>
#include<algorithm>
#define reg register
typedef long long ll;

const int maxn = 100005;

int Test_cnt;
int T;
int N;
int M;
int K;
int cnt[maxn];

void Work(){
        int a, c;
        scanf("%d%d%d%d%d", &a, &N, &c, &M, &K);
        if(!K || K > N || K > M){ printf("0\n"); return ; }
        int Lim = std::min(N, M);
        for(reg int d = Lim; d >= K; d --){
                cnt[d] = (N/d) * (M/d);
                for(reg int i = 2; i*d <= Lim; i ++) cnt[d] -= cnt[i*d];
        }
        printf("Case %d: %d\n", ++ Test_cnt, cnt[K]);
}

int main(){
        scanf("%d", &T);
        while(T --) Work();
        return 0;
}


正 解 部 分 正解部分
然而上面的方法连样例都过不去 , 为什么呢?
假设 N < M N<M N<M, 因为在 N N N M M M 的重合部分会有重复计算,
所以要减去这些重复的计算, 再计算一次 N N N N N N 之间的答案, 除 2 2 2后与 N N N M M M得出的答案作差即可.

时间复杂度同上 .


实 现 部 分 实现部分
没什么好说的 .

#include<cstdio>
#include<algorithm>
#define reg register
typedef long long ll;

const int maxn = 200005;

int Test_cnt;
int T;
ll N;
ll M;
ll K;
ll cnt[maxn];

void Work(){
        ll a, c;
        scanf("%lld%lld%lld%lld%lld", &a, &N, &c, &M, &K);
        if(K > N || K > M){ printf("Case %d: 0\n", ++ Test_cnt); return ; }
        ll Lim = std::min(N, M);
        for(reg ll d = Lim; d >= 1; d --){
                cnt[d] = (N/d) * (M/d);
                for(reg ll i = 2; i*d <= Lim; i ++) cnt[d] -= cnt[i*d];
        }
        ll Ans = cnt[K];
        for(reg ll d = Lim; d >= 1; d --){
                cnt[d] = (Lim/d) * (Lim/d);
                for(reg ll i = 2; i*d <= Lim; i ++) cnt[d] -= cnt[i*d];
        }
        printf("Case %d: %lld\n", ++ Test_cnt, Ans-(cnt[K]>>1));
}

int main(){
        scanf("%d", &T);
        while(T --) Work();
        return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值