HDU4135 Co-prime [容斥]

15 篇文章 0 订阅
8 篇文章 0 订阅

C o − p r i m e Co-prime Coprime


D e s c r i p t i o n \mathcal{Description} Description
求区间 L , R L,R L,R 之间与 N N N 互质的数的个数 , L , R &lt; = 1 0 15 , N &lt; = 1 0 9 L,R&lt;=10^{15}, N&lt;=10^9 L,R<=1015,N<=109


最 初 想 法 最初想法
N N N 分解质因数, 得出 c n t cnt cnt 个不同的质因数, 存到 p [ ] p[] p[] 数组中.
利用 容斥 计算 [ 1 , x ] [1,x] [1,x] 中与 N N N不互质的数的数量 n u m num num, 互质的数量则为 x − n u m x-num xnum.
使用 [ 1 , L − 1 ] , [ 1 , R ] [1,L-1],[1,R] [1,L1],[1,R] 的答案再 容斥 一下就可以了 .

A t t e n t i o n Attention Attention

  • 分 解 质 数 到 最 后 时 , N 可 能 为 最 后 的 大 质 数 , 需 要 计 入 p [ ] 分解质数到最后时, N可能为最后的大质数, 需要计入p[] ,N,p[]
  • 在容斥找 n u m num num 时需要 求解 L C M LCM LCM, 不能莽乘.
  • DFS容斥时需要 判断是否一个数都没有选.

D F S DFS DFS 部分:

void DFS(int k, bool opt, int p_sum, const int &x){
        if(k == cnt+1){
                if(p_sum == 1) return ;
                if(!opt) Tmp_1 -= x/p_sum;
                else Tmp_1 += x/p_sum;
                return ;
        }
        DFS(k+1, opt^1, Lcm(p_sum, p[k]), x);
        DFS(k+1, opt, p_sum, x);
}

正 解 部 分 正解部分
按以上解法, 将 D F S DFS DFS 换成 二 进 制 枚 举 二进制枚举 A C AC AC 了, 好奇怪.

症结 ↑

二进制枚举状态部分:

        for(reg int i = 1; i < 1<<cnt; i ++){
                int p_sum = 1;
                int p_cnt = 0;
                for(reg int j = 1; j <= cnt; j ++)
                        if((1<<j-1) & i) p_cnt ++, p_sum = Lcm(p_sum, p[j]);
                if(p_cnt & 1) Tmp_1 += x/p_sum;
                else Tmp_1 -= x/p_sum;
        }
        return x-Tmp_1;


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

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

const int maxn = 105;

int Test_Cnt;
int N;
int cnt;
int p[maxn];
ll Tmp_1;
ll L;
ll R;

int Gcd(int a, int b){ return !b?a:Gcd(b, a%b); }
int Lcm(int a, int b){ return a/Gcd(a, b)*b; }

ll Calc(ll x){
        if(!x) return 0;
        Tmp_1 = 0;
        for(reg int i = 1; i < 1<<cnt; i ++){
                int p_sum = 1;
                int p_cnt = 0;
                for(reg int j = 1; j <= cnt; j ++)
                        if((1<<j-1) & i) p_cnt ++, p_sum = Lcm(p_sum, p[j]);
                if(p_cnt & 1) Tmp_1 += x/p_sum;
                else Tmp_1 -= x/p_sum;
        }
        return x-Tmp_1;
}

void Work(){
        scanf("%lld%lld%d", &L, &R, &N);
        cnt = 0;
        for(reg int d = 2; d*d <= N; d ++)
                if(N % d == 0){
                        p[++ cnt] = d;
                        while(N%d == 0) N /= d;
                }
        if(N > 1) p[++ cnt] = N; //Attention
        printf("Case #%d: %lld\n", ++ Test_Cnt, Calc(R) - Calc(L-1));
}

int main(){
        int T;
        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、付费专栏及课程。

余额充值