HDU3366Coprime 二分法+容斥原理

HDU题目链接

题意

求第k个与n,m都互素的正数(0 < m, n, k <= 10^9)

题解

使用二分法查找元素
二分0~(1LL<<maxn),找到x满足[1,x]中只有k个与n,m互素的数。即将 “ 求第k个与n,m都互素的正数 " 转化为" 求[1,x]中与n,m的互素个数 ”。
求[1,x]中与n,m的互素个数
用容斥原理求[1,x]中可以被几个数整除的数的个数

  1. 预处理求素数(素数筛模板 longlong)
  2. 求n,m因子:遍历素数分别求n,m的因子(不包含1,n,m)+使用unique()去重。
  3. ans = SUM(X/奇数个因子之积)-SUM(X/偶数个因子之积)
    例:[1,10]中可以被2,3整除的数有 10/2+10/3-10/6

注意点

  1. 范围是long long 凡是与long long 类型进行运算(比较,加减乘除,赋值)的都要设置为long long 变量。(1LL<<maxn,long long i)
  2. long long double 数组最多开1e6
  3. 二分法查找元素:判断条件/循环边界 的取等
  4. 求互素个数遍历边界 i*i <= n(小于等于)

代码积累

  1. 欧拉素数筛
long long prime[MAXN], tot;
long long check[MAXN];
void eular(){
    memset(check, 0, sizeof(check));
    tot = 0;
    for(int i = 2; i<MAXN; i++){
        if(!check[i])
            prime[tot++] = i;
         for(int j = 0; j<tot && prime[j]*i<MAXN; j++){
            check[i*prime[j]] = 1;//素数的倍数为合数
            if(i%prime[j] == 0) break;
        }
    }
}
  1. 求一个数的因子
for(long long i = 0;prime[i]*prime[i] <=m; i++){
        if(m%prime[i] == 0){
            fac[cnt1++] = prime[i];
            while(m%prime[i] == 0) m/=prime[i];
        }
    }
    if(m > 1) fac[cnt1++] = m;
  1. 用容斥原理求[1,x]中可以被几个数整除的数的个数(状态压缩遍历各种选择)
long long RC(long long x){
    long long ret = 0, tmp, cnt;
    for(long long  i = 1; i<(1LL<<cnt1); i++){
        tmp = 1, cnt = 0;
        for(long long j = 0; j<cnt1; j++){
            if(i&(1LL<<j)){
                tmp *=fac[j];
                cnt++;
            }
        }
        if(cnt&1) ret += x/tmp;
        else ret -= x/tmp;
    }
    return ret;
}

AC代码

#include <bits/stdc++.h>
using namespace std;
#define MAXN 1000005
long long fac[MAXN], cnt1;
long long prime[MAXN], tot;
long long check[MAXN];
long long n, m, k;
void eular(){
    memset(check, 0, sizeof(check));
    int tot = 0;
    for(int i = 2; i<MAXN; i++){
        if(!check[i])
            prime[tot++] = i;
         for(int j = 0; j<tot && prime[j]*i<MAXN; j++){
            check[i*prime[j]] = 1;//素数的倍数为合数
            if(i%prime[j] == 0) break;
        }
    }
}
void getFac(long long n, long long m){
    cnt1 = 0;
    for(long long i = 0;prime[i]*prime[i] <=n; i++){
        if(n%prime[i] == 0){
            fac[cnt1++] = prime[i];
            while(n%prime[i] == 0) n/=prime[i];
        }
    }
    if(n > 1) fac[cnt1++] = n;
    for(long long i = 0;prime[i]*prime[i] <=m; i++){
        if(m%prime[i] == 0){
            fac[cnt1++] = prime[i];
            while(m%prime[i] == 0) m/=prime[i];
        }
    }
    if(m > 1) fac[cnt1++] = m;
    sort(fac, fac+cnt1);
    int tmp = unique(fac, fac+cnt1) - fac;
    cnt1 = tmp;
}
//状态压缩所有的因子组合,[1,x]中不是互素的数有x/因子*因子,容斥原理排除重复
long long RC(long long x){
    long long ret = 0, tmp, cnt;
    for(long long  i = 1; i<(1LL<<cnt1); i++){
        tmp = 1, cnt = 0;
        for(long long j = 0; j<cnt1; j++){
            if(i&(1LL<<j)){
                tmp *=fac[j];
                cnt++;
            }
        }
        if(cnt&1) ret += x/tmp;
        else ret -= x/tmp;
    }
    return ret;
}
long long DS(){
    long long l = 0, r = (1LL<<62), mid, ans;
    while(l <= r){
        mid = (l+r) / 2;
        if(mid - RC(mid) >= k){
            ans = mid;
            r = mid - 1;
        }else{
            l = mid + 1;
        }
    }
    return ans;
}
int main(int argc, char const *argv[])
{
    eular();
    int t;scanf("%d", &t);
    for(int cnt = 1; cnt<=t; cnt++){
        scanf("%lld%lld%lld", &m, &n, &k);

        getFac(m, n);
        printf("Case %d: %lld\n", cnt,DS());

    }
    return 0;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值