容斥定理的應用
容斥定理概念:
在計數時,必須注意沒有重複,沒有遺漏。為了使重疊部分不被重複計算,人們研究出一種新的計數方法,這種方法的基本思想是:先不考慮重疊的情況,把包含於某內容中的所有物件的數目先計算出來,然後再把計數時重複計算的數目排斥出去,使得計算的結果既無遺漏又無重複,這種計數的方法稱為容斥原理。
今天講利用二進位制狀態列舉的方法;
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
題大意就是給你一個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;
}
第一遍可能看不太明白,多看幾遍,學程式設計就是要有耐心,要不厭其煩得去看 反覆去看,才有可能成為大神,加油吧 少年