1. 程式人生 > >容斥定理的應用

容斥定理的應用

容斥定理概念:

計數時,必須注意沒有重複,沒有遺漏。為了使重疊部分不被重複計算,人們研究出一種新的計數方法,這種方法的基本思想是:先不考慮重疊的情況,把包含於某內容中的所有物件的數目先計算出來,然後再把計數時重複計算的數目排斥出去,使得計算的結果既無遺漏又無重複,這種計數的方法稱為容斥原理。

今天講利用二進位制狀態列舉的方法;

    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;

}

第一遍可能看不太明白,多看幾遍,學程式設計就是要有耐心,要不厭其煩得去看 反覆去看,才有可能成為大神,加油吧 少年