1. 程式人生 > >容斥原理(二進位制列舉)

容斥原理(二進位制列舉)

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

兩個集合的容斥關係公式: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