容斥原理

给定a[1],a[2],a[3],...a[m],求1到n的整数中至少能整除a中一个元素的数有几个?

限制条件:

1<=n<=10^9

1<=m<=15

样例:

输入

n=100,m=2

a={2,3}

输出

67(2的倍数有50个,3的倍数有33个,6的倍数有16个,因此50+33-16=67个)


思路:如果对于1到n中的每个数都判断是否满足条件,那么复杂度是O(n*m),无法在时间限制之内得出答案。由于m的范围比较小,我们可以试着对这一条件加以利用。

首先我们考虑一下m=1的情况,答案是n/a[1];当m=2时,n/a[1]+n/a[2]-n/lcm(a[1],a[2]);依次类推,就可以发现对于一般情况的计算方法。

下面我们就来试着写出一般情况的计算公式,假设A[i](1<=i<=m)是X的一些子集,我们要求|A[i]|。例如在上面的问题里,就可以看成A[i]={n|n可以整除a[i]}。此时,有如下等式成立:|∪A[i]|=∑|A[i]|(1<=i<=m)-∑|A[i]∩A[j]|(1<=i<j<=m)+∑|A[i]∩A[j]∩A[k]|(1<=i<j<k<=m)-...+(-1)^(m+1)∑|A[1]∩A[2]∩...∩A[m]|

这个式子被称作容斥原理。一般在集合的个数m比较小,并且|A[i]∩A[j]∩...∩A[k]|这些项容易计算时适合使用容斥原理。


时间复杂度:假设计算一个|A[i]∩A[j]∩...∩A[k]|需要花费O(f)的时间,那么总共就需要花费O(2^m*f)的时间


代码:

typedef long long ll;

int a[max_m];
int n,m;

int gcd(int a,int b)  
{  
        if(b==0)   return a;  
        return   gcd(b,a%b);  
}  
      
void solve(){    
        ll res=0;    
        for(int i=1;i<(1<<m);i++)    
        {    
                 int num=0;    
                 for(int j=1;j!=0;j>>=1)    
                            num+=j&1;//i的二进制表示中1的数量    
                 ll lcm=1;    
                 for(int j=0;j<m;j++)    
                 {    
                           if(i>>j&1)    
                           {    
                                     lcm=lcm/gcd(lcm,a[j])*a[j];    
                                     //如果lcm大于n,则n/lcm=0.因此在溢出之前break。    
                                     if(lcm>n)    
                                                break;    
                             }    
                    }    
                    if(num%2==0)    
                               res-=n/lcm;    
                    else    
                                res+=n/lcm;    
         }    
         printf("%d\n",res);    
}   





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值