容斥原理(二進位制列舉)
在計數時,必須注意無一重複,無一遺漏。為了使重疊部分不被重複計算,人們研究出一種新的計數方法,這種方法的基本思想是:先不考慮重疊的情況,把包含於某內容中的所有物件的數目先計算出來,然後再把計數時重複計算的數目排斥出去,使得計算的結果既無遺漏又無重複,這種計數的方法稱為容斥原理。
兩個集合的容斥關係公式:A∪B = A+B – A∩B (∩:重合的部分)
三個集合的容斥關係公式:A∪B∪C = A+B+C – A∩B – B∩C – C∩A +A∩B∩C
詳細推理如下:
1、 等式右邊改造 = {[(A+B – A∩B)+C – B∩C] – C∩A }+ A∩B∩C
2、文氏圖分塊標記如右圖圖:1245構成A,2356構成B,4567構成C
3、等式右邊()裡指的是下圖的1+2+3+4+5+6六部分:
那麼A∪B∪C還缺部分7。
4、等式右邊[]號裡+C(4+5+6+7)後,相當於A∪B∪C多加了4+5+6三部分,
減去B∩C(即5+6兩部分)後,還多加了部分4。
5、等式右邊{}裡減去C∩A (即4+5兩部分)後,A∪B∪C又多減了部分5,
則加上A∩B∩C(即5)剛好是A∪B∪C。
在這裡我要說的是一個二進位制列舉。
正文
所謂二進位制列舉 就是一種暴力的方式,用0,1來代表一個數字存在或不存在。
例如 0101 可以理解為 第一個物品要了,第二個不要,第三個要了,第四個不要。
那麼這個和容斥原理有什麼關係呢?
我們都知道容斥原理的公式為:
可能很多剛剛接觸的小夥伴不太瞭解,其實有很多的問題需要容斥來解決,我舉一個例子:
如果求100以內的除了 2的倍數 和 3的倍數的數 有多少個,我們可以很容易知道 2的倍數的個數是100/2,而3的倍數是100/3,“答案“是100-50-33 = 17
這顯然是不對的 以6為例 6既是2的倍數也是三的倍數,顯然減重了。 但是我們有人就要說了,我可以每個數都判斷一下。
#include<iostream>
using namespace std;
int main(){
int ans = 0;
for(int i = 1;i<+100;i++){
if(i%2==0 || i%3==0){
ans++;
}
}
cout<<ans<<endl;
}
但是,當n很大的時候,要O(n)時間複雜度。顯然是不符合實際的。
這裡我先引入容斥原理的概念
我們回到剛才的那個問題,我們有沒有辦法解決減重的情況?
這裡我們就要運用容斥原理解決這個問題了,
100-2的倍數-3的倍數+6的倍數
減重複的加回來就好了,這個6是二和三的最小公倍數
好了這個問題我們已經很好的解決了,但是我們真的解決問題了嗎?
我們在舉一個例子 :
100以內的數 求 2 3 5 7 11 的倍數,我們還是用剛才方法,
2的倍數+3的倍數+5的倍數+7的倍數+11的倍數- 。。。。。。。
這個時候我們犯難了 要減的有2∩3,減2∩5......還要加回減多的2∩3∩5.....
太麻煩了,明明這麼簡單的一道題,怎麼解決?
這裡我們就要引入二進位制列舉的概念了:
根據上面的公式:
上面的公式有2^m-1種情況。
我們把二進位制表示2,3,5,7,11五個數。為什麼可以這樣呢?
這就是二進位制列舉的好處了。因為有5個數,所以5個數組成的倍數有2^5-1種。
比如2的倍數:我們表示為00001
2∩3的倍數:00011
5∩11的倍數:10100
就是說,1~2^5每一個二進位制位(0表示沒選,1表示選定)表示2,3,5,7,11的選不選定。初學者還是有點難理解的,認認真真看程式碼,看是怎麼實現的,花點時間認真弄明白。
#include<iostream>
#include<stdio.h>
#include<math.h>
using namespace std;
int a[] = {2,3,5,7,11};
int main(){
int n;
int lena = 5;
while(~scanf("%d",&n)){
int ans = 0;
for(int i = 1;i<(1<<lena);i++){
///每個數都有2種狀態 ,在或不在 在本題中的意思是2,3,5,7,11的倍數或不是,
///一共有2^5-1種可能
int tmp=0;
int lcm=1;
for(int j = 0;j<lena;j++){
if((1<<j)&i){///選第j個b倍數子;與i的二進位制的第j位比較,看是否為1,是則選中
tmp++;//標誌變數計算i中1的個數,也就是倍數的個數
lcm*=a[j];///相乘
}
}
if(tmp%2==1) ans+=n/lcm; //奇數是加 偶數是減
else ans-=n/lcm;printf("%d\n",ans);
}
cout<<ans<<endl;
}
}
簡單應用:
由0 到9 數字組成排列,要求第一個數大於1,最一個數小於8,一共有幾種排列。
思路:
求逆問題,第一個數<=1,最後一個數>=8
由容斥原理,
則有 事件A(第一個數<=1) = 2*9!, 事件B(最後一個數>=8) = 2*9!, 事件C(A交B) C= 2*2*8!
所以逆問題的結果是 A+B-C
則問題的答案是 10!-(A+B-C)。
經典例題:
例題1:
給出一個數N,求1至N中,有多少個數不是2 3 5 7的倍數。 例如N = 10,只有1不是2 3 5 7的倍數。
- 1
- 2
Input
輸入1個數N(1 <= N <= 10^18)。
Output
輸出不是2 3 5 7的倍數的數共有多少。
Sample Input
10
- 1
- 2
Sample Output
1
- 1
- 2
包含排斥原理,就是那道公式。
四個圓圈的並,是四個的單獨面積的總和,減去四個分別兩兩相交的兩層的面積,加上四個分別三三相交的三層的面積,再減去四個相交的面積。最後就是並了。這道題,也是這個意思,只不過數多一點。
#include<stdio.h>
int main(){
long long num=0,n,a,b,c,d,ab,ac,ad,bc,bd,cd,abc,abd,acd,bcd,abcd;
scanf("%lld",&n);
a=n/2;
b=n/3;
c=n/5;
d=n/7;
ab=n/6;
ac=n/10;
ad=n/14;
bc=n/15;
bd=n/21;
cd=n/35;
abc=n/30;
abd=n/42;
acd=n/70;
bcd=n/105;
abcd=n/210;
num=a+b+c+d-ab-ac-ad-bc-bd-cd+abc+abd+acd+bcd-abcd;
printf("%I64d\n",n-num);
}
這次還有就是看資料量可不可以暴力過,可以的話,直接暴力就解決了。這就是考察你會不會進行估算。
自己可以用二進位制列舉寫一下。
相關推薦
容斥原理(二進位制列舉)
在計數時,必須注意無一重複,無一遺漏。為了使重疊部分不被重複計算,人們研究出一種新的計數方法,這種方法的基本思想是:先不考慮重疊的情況,把包含於某內容中的所有物件的數目先計算出來,然後再把計數時重複計算的數目排斥出去,使得計算的結果既無遺漏又無重複,這種計數的方法稱為容斥原理
【專題總結】容斥原理(持續更新)
從動機的角度出發。在用“做減法”的思想解決計數類問題時,可能會遇到“多減去符合條件的數目”,試圖加回來的時候又會遇到“多加上不符合條件的數目”的情況,這時候也許需要用容斥原理來設計計數演算法。 從實現的角度出發。在對事件集合的“並事件”計數遇到困難時,可通
容斥原理(模板+例題)
網上找來方便自己看,理解。 容斥原理:在計數時,必須注意無一重複,無一遺漏。為了使重疊部分不被重複計算,人們研究出一種新的計數方法,這種方法的基本思想是:先不考慮重疊的情況,把包含於某內容中的所有物件
HDU 2204 容斥原理(過程詳解)
Ignatius 喜歡收集蝴蝶標本和郵票,但是Eddy的愛好很特別,他對數字比較感興趣,他曾經一度沉迷於素數,而現在他對於一些新的特殊數比較有興趣。 這些特殊數是這樣的:這些數都能表示成M^K,M和K是正整數且K>1。 正當他再度沉迷的時候,他發現不知道什麼時
【ARC102E】Stop. Otherwise...(容斥原理,動態規劃)
【ARC102E】Stop. Otherwise...(容斥原理,動態規劃) 題面 AtCoder 有\(n\)個骰子,每個骰子有\(K\)個面,上面有\(1\)到\(K\)。骰子都是一樣的。 現在對於\([2,2k]\)中的每一個數\(x\),要求出滿足不存在任意兩個骰子的點數和為\(x\)的方案數。
容斥原理(翻譯)
俄語的。由於文章確實很實用,而且鑑於國內俄文資料翻譯的匱乏,我下決心將其翻譯之。由於俄語對我來說如同亂碼,而用Google直接翻譯中文的話又變得面目全非,所以只能先用Google翻譯成英語,再反覆讀,慢慢理解英語的意思,實在是弄得我頭昏腦脹。因此在理解文章意思然後翻譯成中文的時候,中文都不知道如何表述了。而又
【BZOJ2669】區域性極小值(容斥原理+狀壓dp)
題意:有一個nn行mm列的整數矩陣,其中11到nmnm之間的每個整數恰好出現一次。如果一個格子比所有相鄰格子(相鄰是指有公共邊或公共頂點)都小,我們說這個格子是區域性極小值。給出所有區域性極小值的位置,你的任務是判斷有多少個可能的矩陣。(1<=n<=
hdu3501 尤拉函式(或容斥原理(莫比烏斯函式))
題目連結:http://acm.hdu.edu.cn/showproblem.php?pid=3501 題解1:顯然,本體可以用容斥原理,求出每個數的倍數情況,其係數就是莫比烏斯函式。 題解2:對於整
poj-3279 poj-1753(二進位制列舉)
題目連結:http://poj.org/problem?id=3279 題目大意: 有一個m*n的棋盤(1 ≤ M ≤ 15; 1 ≤ N ≤ 15),每個格子有兩面分別是0或1,每次可以對一個格子做一次翻轉操作,將被操作的格子和與其相鄰的周圍4個格子都會進行翻轉。問做少做多少次翻轉可以將所有格子翻轉
GCD HDU - 1695 (容斥原理 + 尤拉函式)
Given 5 integers: a, b, c, d, k, you're to find x in a...b, y in c...d that GCD(x, y) = k. GCD(x, y) means the greatest common divisor of x and y. Sin
hdu 6021 MG loves string (一道容斥原理神題)(轉)
MG loves string Accepts: 30 Submissions: 67 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 262144/262144 K (Java/Others) 問
2015瀋陽網路賽K(二進位制列舉)
這題看到先想到的是先二進位制列舉選法,然後再全排列,然後再全部符號,然後再加括號。 鐵定涼涼放棄 然後百度的神仙程式碼。 思路是先列舉選法,然後把每個選法分成兩份,每份撲克牌存著各自的能組成所有值以及組成這些值的數量。 然後兩份的撲克牌合起來能的到一整份的撲克
容斥原理的(二進位制思想和質因子分解+模板)
#include<iostream> #include<string> #include<cstdio> #include<algorithm> #include<cmath> #include<iomanip
HDU 4135——Co-prime(容斥原理&&二進位制列舉)
題目連結: 模板 void prime(ll n) { k=0; for(int i=2;i*i<=n;i++) { if(n%i==0) { Prime[k++]=i; while
數學 hdu1796(容斥原理+dfs) / (容斥原理+二進位制列舉)
容斥水題,,但是自己沒見過,賽後學了學 結果=1個數最小公倍數的個數(小於n,後面的都小於n) - 2個數最小公倍數的個數+3個數最小公倍數的個數 直到n; #include<iostream> #include<cstring> #include
hdu1796(二進位制容斥原理基本運用)
給出n,和m個數,求小於n,且可以被m個數中任意一個整除的數有哪些? 就二進位制列舉哪些數,然後取最小公倍數,搞一搞就可以了 #include<cstdio> #include<algorithm> #include<cstring>
【8.12校內測試】【容斥原理】【數位DP(二進位制)】【壓維(?)】
emm,今天測試沒有什麼感想。。 1 a 1.1 問題描述 有n 個青蛙,m 個石頭圍成一圈編號為0 m��1,第i 只青蛙每次跳ai 步,這意味著青 蛙能從石頭j mod m 跳到石頭(j + ai) mod m。青蛙每跳一個石頭,就佔領它。
容斥原理 —— 求1~n有多少個數與k互質(二進位制演算法詳細解釋&模板)
這裡有一道經典的例題,可以看一下:點選開啟連結 這裡的n可能要大於k的,所以不能用尤拉函式去做。 我們首先把k分解質因數,儲存到p陣列中,num表示質因子的數量。 void pr(int k) //求k的質因子 { num = 0; for (int i = 2 ;
HDU 5794 A Simple Chess(楊輝三角+容斥原理+Lucas)
exgcd -i -- || 兩種方法 sizeof put amp mem 題目鏈接 A Simple Chess 打表發現這其實是一個楊輝三角…… 然後發現很多格子上方案數都是0 對於那寫可能可以到達的點(先不考慮障礙點),我們先叫做有
#19. 計數(容斥原理)
cnblogs += void lld ring 輸入輸出 計數 define printf 時間限制:1s 內存限制:256MB 【問題描述】 給出m個數a[1],a[2],…,a[m] 求1~n中有多少數不是a[1],a[2],…,a