昨天做了一個求[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時的解。
我們定義Ak為xk>=9並且其他xi>=0時的集合,同樣我們用上面的添加“夾板”法來計算Ak的大小,因為有9個位置已經被xk所利用了,所以:
然后計算兩個這樣的集合Ak、Ap的交集:
因為所有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)的復雜度。