容斥定理的应用

容斥定理概念:

计数时,必须注意没有重复,没有遗漏。为了使重叠部分不被重复计算,人们研究出一种新的计数方法,这种方法的基本思想是:先不考虑重叠的情况,把包含于某内容中的所有对象的数目先计算出来,然后再把计数时重复计算的数目排斥出去,使得计算的结果既无遗漏又无重复,这种计数的方法称为容斥原理。

今天讲利用二进制状态枚举的方法;

    for(int i = 0; i < (1 << n); i++)  // 相当于枚举所有的情况 o(2^n*n) {
         for(int j = 0; j < n ;j++){
            printf("%d ", (i >> j )& 1);
        }
    }

/*代码解释

a1 a2 a3 a4 ... an 
0  0  0  0       0 
1  1  1  1       1  //就是说取a1就是1,不取就是0

1 2 3 n = 3 //当n=3
1 0 0 
0 1 0 
1 1 0 

0 0 1
1 0 1 
0 1 1
1 1 1// 以上这些即为对应的取哪些数字  个数为2^n-1,也即为上述程序打印出的结果

*/
可能看这些不太理解,现在做一道题就明白了

例题: How many integers can you find

 Now you get a number N, and a M-integers set, you should find out how many integers which are small than N, that they can divided exactly by any integers in the set. For example, N=12, and M-integer set is {2,3}, so there is another set {2,3,4,6,8,9,10}, all the integers of the set can be divided exactly by 2 or 3. As a result, you just output the number 7.

Input

  There are a lot of cases. For each case, the first line contains two integers N and M. The follow line contains the M integers, and all of them are different from each other. 0<N<2^31,0<M<=10, and the M integer are non-negative and won’t exceed 20.

Output

  For each case, output the number.

Sample Input

12 2 2 3
Sample Output
7

hdu 1796 http://acm.hdu.edu.cn/showproblem.php?pid=1796

题大意就是给你一个N,然后再给你M个数据,求小于N的数里面是M个数里任意k(1<=k<=m)个数的公倍数的个数

举个例子吧 比如样例,N=12,M=2,M个数 分别为 2,3;k=1或2,当k=1时,小于N的数是2的倍数是(2,4,6,8,10)是3的倍数的是(3,6,9,)k=2时 小于N的数是2和3公倍数的是(6)所以总共有5+3-1=7个;

这里用到了算术基本原理的·一个知识:

要计算几个集合并集的大小,我们要先将所有单个集合的大 小计算出来,然后减去所有两个集合相交的部分,再加回所 有三个集合相交的部分,再减去所有四个集合相交的部分, 依此类推,一直计算到所有集合相交的部分,

简单的记就是奇加偶减,

该上代码了,结合理解更容易记住

代码如下:

#include<stdio.h>

typedef long long ll;

ll a[1000003],b[1000003];

ll gcd(ll a,ll b) {

    return b?gcd(b,a%b):a; } //利用欧几里得求最大公约数

ll lc(ll a,ll b) {

    return a/gcd(a,b)*b; } //求最小公倍数

ll AA(ll m,ll n)

{

    ll i,j,res=0,k=0;

for(i=0;i<m;i++) {

    scanf("%lld",&a[i]); //输入

   if(a[i]!=0) b[k++]=a[i]; //把0去掉

}

for( i=1;i<(1<<m);i++)//利用二进制状态枚举

{

    ll ans=0,lcm=1;

    for(j=0;j<m;j++)

    {

        if(1&(i>>j)) {

        ans++; lcm=lc(lcm,b[j]); //ans记录取出的个数

         }

     }

    if(1&ans) res+=(n-1)/lcm;

    else res-=(n-1)/lcm; //奇加偶减,(n-1)/lcm就是小于n的数中 b[j]的个数

}

    return res;

}

int main()

{

    ll n,m;

    while(~scanf("%lld %lld",&n,&m)) 

        printf("%lld\n",AA(n,m));

return 0;

}

第一遍可能看不太明白,多看几遍,学编程就是要有耐心,要不厌其烦得去看 反复去看,才有可能成为大神,加油吧 少年

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值