容斥原理


昨天做了一個求[1,n]里能被[2,m]中的數整除的個數,就去搜容斥原理,找到一篇講得特別好的博客,就轉載了其中一部分過來。

轉載:https://blog.csdn.net/m0_37286282/article/details/78869512

關於集合的原理公式

      上述描述的公式形式可以表示如下:
       

        

 

      它可以寫得更簡潔一些,我們將B作為所有Ai的集合,那么容斥原理就變成了:

        

  這個公式是由 De Moivre (Abraham de Moivre)提出的。

 

關於概率論的原理

       設事件 代表發生某些事件的概率(即發生其中至少一個事件的概率),則:

  

         這個公式也可以用B代表Ai的集合:

 

 

對於實際問題的應用

 

一個簡單的排列問題

 

       由0到9的數字組成排列,要求第一個數大於1,最后一個數小於8,一共有多少種排列?

 

         我們可以來計算它的逆問題,即第一個元素<=1或者最后一個元素>=8的情況。

 

         我們設第一個元素<=1時有X組排列,最后一個元素>=8時有Y組排列。那么通過容斥原理來解決就可以寫成:

 

       

 

         經過簡單的組合運算,我們得到了結果:

 

         

 

         然后被總的排列數10!減,就是最終的答案了。

 

 

(0,1,2)序列問題

 

       長度為n的由數字0,1,2組成的序列,要求每個數字至少出現1次,這樣的序列有多少種?

 

         同樣的,我們轉向它的逆問題。也就是不出現這些數字的序列 不出現其中某些數字的序列。

 

         我們定義Ai(i=0…2)表示不出現數字i的序列數,那么由容斥原理,我們得到該逆問題的結果為:

 


 

           可以發現每個Ai的值都為2^n(因為這些序列中只能包含兩種數字)。而所有的兩兩組合都為1(它們只包含1種數字)。最后,三個集合的交集為0。(因為它不包含數字,所以不存在)

 

        要記得我們解決的是它的逆問題,所以要用總數減掉,得到最終結果:

 

         

 

 

方程整數解問題

 

       給出一個方程:

 

       

 

         其中

 

        求這個方程的整數解有多少組。

 

        我們先不去理會xi<=8的條件,來考慮所有正整數解的情況。這個很容易用組合數來求解,我們要把20個元素分成6組,也就是添加5塊“夾板”,然后在25個位置中找5塊“夾板”的位置。

 

         

 

         然后通過容斥原理來討論它的逆問題,也就是x>=9時的解。

 

         我們定義Akxk>=9並且其他xi>=0時的集合,同樣我們用上面的添加“夾板”法來計算Ak的大小,因為有9個位置已經被xk所利用了,所以:

 

         

 

         然后計算兩個這樣的集合AkAp的交集:

 

         

 

         因為所有x的和不能超過20,所以三個或三個以上這樣的集合時是不能同時出現的,它們的交集都為0。最后我們用總數剪掉用容斥原理所求逆問題的答案,就得到了最終結果:

 

         

 

 

求指定區間內與n互素的數的個數:

 

       給出整數n和r。求區間[1;r]中與n互素的數的個數。

 

         去解決它的逆問題,求不與n互素的數的個數。

 

         考慮n的所有素因子pi(i=1…k)

 

         [1;r]中有多少數能被pi整除呢?它就是:

 

       

 

         然而,如果我們單純將所有結果相加,會得到錯誤答案。有些數可能被統計多次(被好幾個素因子整除)。所以,我們要運用容斥原理來解決。

 

         我們可以用2^k算法求出所有的pi組合,然后計算每種組合的pi乘積,通過容斥原理來對結果進行加減處理。

 

         關於此問題的最終實現:

  

 1 int solve (int n, int r) {
 2         vector<int> p;
 3         for (int i=2; i*i<=n; ++i)
 4                if (n % i == 0) {
 5                        p.push_back (i);
 6                        while (n % i == 0)
 7                                n /= i;
 8                }
 9         if (n > 1)
10                p.push_back (n);
11         int sum = 0;
12         for (int msk=1; msk<(1<<p.size()); ++msk) {
13                int mult = 1,bits = 0;
14                for (int i=0; i<(int)p.size(); ++i)
15                        if (msk & (1<<i)) {
16                                ++bits;
17                                mult *= p[i];
18                        }
19                int cur = r / mult;
20                if (bits % 2 == 1)
21                        sum += cur;
22                else
23                        sum -= cur;
24         }
25         return r - sum;
26 }
27  

 

算法的復雜度為 

求在給定區間內,能被給定集合至少一個數整除的數個數

       給出n個整數ai和整數r。求在區間[1;r]中,至少能被一個ai整除的數有多少。

         解決此題的思路和上題差不多,計算ai所能組成的各種集合(這里將集合中ai的最小公倍數作為除數)在區間中滿足的數的個數,然后利用容斥原理實現加減。

         此題中實現所有集合的枚舉,需要2^n的復雜度,求解lcm需要O(nlogr)的復雜度。

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM