牛客小白月赛5 A-无关(relationship)

  这是我第一次写容斥定理的题,题目就是让你求[l,r]区间和集合a无关的数的个数,一个数若与集合a无关,则集合a里任何一个数都不是他的因数,也就是该数不是集合a里任何一个数的倍数,正难则反,所以我们可以先求出反面的,也就是和集合a有关的数的个数,和集合a有关的数要满足一个条件:至少要是集合a里一个数的倍数,求出反面之后总个数-和集合a有关的数的个数即是答案了。这里每个数ai都对应着一个集合,集合里的数就是在区间[l,r]范围内ai的全部倍数,这些倍数的数量就是[l,r]内和集合a有关的数的数量,然后我们求出k个集合的元素个数总和之后会有一些数重复了,比如6是2又是3的倍数,那么我们求在k个集合的元素个数总和就会把6加多了一次,当然有些数可能加多了不止一次,这时候就要用容斥定理来处理了

#include<stdio.h>
//#include<bits/stdc++.h>
//using namespace std;
long long a[25];
int k;
long long solve(long long n)//求1到n区间和集合a有关的数的个数
{
    if(n==0) return 0;//特判一下1到0区间不存在,所以满足条件的数的个数当然是0
    long long ans=0,last=(1<<k)-1;//用二进制数的二进制位表示集合a里每个数的使用状态,1表示使用了,0表示没使用,对应二进制的第一位对应的是第一个数的使用状态,last表示的是k个数的最后一个枚举方式1..1,(k个1)
    for(long long sta=1;sta<=last;sta++)//这里从状态从1枚举到last,刚好把k个数的全部状态枚举了一遍
    {
        int cnt=0;//记录当前枚举的状态里使用的数的个数
        int flag=0;
        long long tmp=1;//tmp存的是枚举到的数的最小公倍数
        for(int j=0;j<k;j++)
            if(sta&(1<<j))
            {
                cnt++;
                tmp*=a[j];
                if(tmp>n) {flag=1;break;}//

            }
        if(flag) continue;
        if(cnt&1)//根据容斥定理,求k个集合的并集的元素个数时是奇加偶减
        {
            ans+=n/tmp;//我们要求的是这cnt个数,假设是a2,a4,a5,要求这些数在1到n的公共倍数有多少个,就要先求出这cnt个数的lcm然后n/lcm即可,这里题目保证了a数组每个数都是质数,所以cnt个数的乘积即是对应的lcm
        }
        else
        {
            ans-=n/tmp;
        }


    }
     //cout<<"1到"<<n<<"区间里与集合a有关的数的个数:"<<ans<<endl;
    return ans;

}
int main()
{
    long long l,r;
    scanf("%lld%lld%d",&l,&r,&k);
    //cout<<endl;
    //cout<<"l:"<<l<<" "<<"r:"<<r<<" "<<"k:"<<k<<endl;
    for(int i=0;i<k;i++)
        scanf("%lld",&a[i]);
    //cout<<endl;
    //cout<<"a:";
    //for(int i=0;i<k;i++)
        //printf("%lld ",a[i]);
    printf("%lld\n",r-l+1-(solve(r)-solve(l-1)));
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值