二进制枚举(容斥原理)

The Sports Association of Bangladesh is in great problem with their latest lottery ‘Jodi laiga Jai’. There

are so many participants this time that they cannot manage all the numbers. In an urgent meeting they
have decided that they will ignore some numbers. But how they will choose those unlucky numbers!!!
Mr. NondoDulal who is very interested about historic problems proposed a scheme to get free from
this problem.
You may be interested to know how he has got this scheme. Recently he has read the Joseph’s
problem.
There are N tickets which are numbered from 1 to N. Mr. Nondo will choose M random numbers
and then he will select those numbers which is divisible by at least one of those M numbers. The
numbers which are not divisible by any of those M numbers will be considered for the lottery.
As you know each number is divisible by 1. So Mr. Nondo will never select 1 as one of those M
numbers. Now given N, M and M random numbers, you have to find out the number of tickets which
will be considered for the lottery.

Input

Each input set starts with two Integers N (10 ≤ N < 2 31 ) and M (1 ≤ M ≤ 15). The next line will
contain M positive integers each of which is not greater than N.
Input is terminated by EOF.

Output
Just print in a line out of N tickets how many will be considered for the lottery.

Sample Input
10 2
2 3
20 2
2 4

Sample Output
3
10


二进制枚举,前提是得知道容斥原理:

两个集合的容斥关系公式:A∪B =|A∪B| = |A|+|B| - |A∩B |(∩:重合的部分)
三个集合的容斥关系公式:|A∪B∪C| = |A|+|B|+|C| - |A∩B| - |B∩C| - |C∩A| + |A∩B∩C|
然后二进制枚举遍历来查找所有情况!说说个人对二进制枚举的理解:就好比这题:最多15个因子,每个因子只有被我们选或者不被我们选,那么就类似于这种只有两种状态的并且数据量还较小的题就可以来二进制枚举,而且不容易错,如果理解了还会觉得比较好写!而且这种二进制枚举的题目我们能算出来他总共的情况,可以直接判断能否暴力枚举,所以对初学者首次接触有点难以理解,如果自己想通了就好了!

#include <iostream>
#include<algorithm>
#include<memory.h>
using namespace std;
typedef long long ll;
ll x[20];
ll lcm(ll a,ll b)
{
    ll c=a*b;
    if(a<b)swap(a,b);
    ll t=a%b;
    while(t!=0)
    {
        a=b,b=t,t=a%b;
    }
    return c/b;
}

int main()
{
    ll n;
    int m;
    while(cin>>n>>m)
    {
        memset(x,0,sizeof(x));
        ll a[20];
        for(int i=0;i<m;i++)
            cin>>a[i];
        for(int i=1;i<(1<<m);i++)
        {
            int t=i;
            ll lcmm=1;
            int s=0,l,ss=0;
            while(t!=0)
            {
                l=t%2;
                ss++;
                t/=2;
                if(l==1)
                {
                    lcmm=lcm(lcmm,a[ss-1]);
                    s++;
                }
                //cout<<l<<' ';
            }
          //  cout<<"  geshu:  "<<n/lcmm;
          //  cout<<endl;
            x[s-1]+=(n/lcmm);
        }
        ll ans=0;
        for(int i=0;i<m;i++)
        {
            if(i%2==0)
            ans+=x[i];
            else ans-=x[i];
        }
        cout<<n-ans<<endl;
    }
    return 0;
}


转载于:https://www.cnblogs.com/martinue/p/5490558.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值