HDU 4135(容斥原理)

题目链接:点击打开链接


题目大意:求出a~b中与n互质的数字的个数


题目思路:菜鸡之前不会容斥原理以及快速将一个数字分解成质因数..流下了没有技术的泪水。这道题首先先转换一下,从求a~b中与n互质的数字个数转换成求1~b中与n互质的数字个数-1~a-1中与n互质的数字个数。然后互质的个数是由数字总数-与n不互质的数字,比如1~b中与n互质的数字个数=b-1~b中与n不互质的数字个数。

第一步,求出n的质因数。为什么要求呢?先别急第二步讲怎么用n的质因数,这里先讲求法。就是i从2开始,如果i*i大于n就停止,然后遇到n%i以后,在n%i==0的情况下疯狂除i,保证剩下的数字不可以再被i整除,同时此时i是n的一个质因数。

第二步,求1~b中与n不互质的数字个数。与n不互质的数字一定有一个特点,那就是这些数字的质因数一定跟n的质因数有公共部分。我们知道,1~b中能被x整除的数字总数为x/b,比如6/3=2,符合要求的数字为3 6,6/2=3,符合要求的数字为2 4 6,然后根据容斥原理,我们需要奇加偶减,因为如果我们只是单纯的把x/b全加起来,会导致有公共部分加了两次,导致结果错误,需要把这部分去掉,所以要用到容斥原理的性质。容斥原理一共有三种实现方法,dfs,队列数组,位运算。这里采用位运算。

位运算的核心代码是

ll solve(ll num){
    ll temp,ans=0,flag;
    for(ll i=1;i<1<<m;i++){
        temp=1,flag=0;
        for(ll j=0;j<m;j++){
            if(i&1<<j){
                temp*=prime[j],flag++;
            }
        }
        if(flag&1){//奇加偶减
            ans+=num/temp;
        }
        else{
            ans-=num/temp;
        }
    }
    return ans;
}

位运算看的确实特别令人头大..我找了很多东西还是没看懂是怎么实现的,但是我知道这东西做了什么事情..根据容斥原理,当两个数字时,A+B-AB,三个数字时,A+B+C-AB-AC-BC+ABC,分别是3项和7项,没错,2^k-1项,所以i从1遍历到2^m-1处,flag记录几个数字相乘。然后里面那层i&1<<j配合从0遍历到m好像直接能够实现所有容斥原理所需要的那种搭配,虽然不知道为啥,记住好了..

第三步,b-1~b中跟n不互质的数字个数-(a-1-1~a-1中跟n不互质的数字个数)就美滋滋啦(别忘了输出case #1...)

以下是代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
ll prime[1005],m;
void qiuyin(ll n){
    m=0;
    for(ll i=2;i*i<=n;i++){
         if(n%i==0){
            while(n%i==0){
                n/=i;
            }
            prime[m++]=i;
         }
    }
    if(n>1){
        prime[m++]=n;
    }
}
ll solve(ll num){
    ll temp,ans=0,flag;
    for(ll i=1;i<1<<m;i++){
        temp=1,flag=0;
        for(ll j=0;j<m;j++){
            if(i&1<<j){
                temp*=prime[j],flag++;
            }
        }
        if(flag&1){
            ans+=num/temp;
        }
        else{
            ans-=num/temp;
        }
    }
    return ans;
}
int main(){
    int t,tot=1;
    scanf("%d",&t);
    while(t--){
        ll a,b,n;
        scanf("%lld%lld%lld",&a,&b,&n);
        qiuyin(n);
        printf("Case #%d: %lld\n",tot++,((b-solve(b))-(a-1-solve(a-1))));
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值