容斥定理概念:
在计数时,必须注意没有重复,没有遗漏。为了使重叠部分不被重复计算,人们研究出一种新的计数方法,这种方法的基本思想是:先不考虑重叠的情况,把包含于某内容中的所有对象的数目先计算出来,然后再把计数时重复计算的数目排斥出去,使得计算的结果既无遗漏又无重复,这种计数的方法称为容斥原理。
今天讲利用二进制状态枚举的方法;
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;
}
第一遍可能看不太明白,多看几遍,学编程就是要有耐心,要不厌其烦得去看 反复去看,才有可能成为大神,加油吧 少年