这是我第一次写容斥定理的题,题目就是让你求[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
",r-l+1-(solve(r)-solve(l-1)));
return 0;
}