1. 程式人生 > >容斥原理(模板+例題)

容斥原理(模板+例題)

網上找來方便自己看,理解。

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

也可表示為 設S為有限集,    ,則 由於 所以 兩個集合的容斥關係公式:A∪B =|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。

常用方法有兩種:遞迴法和二進位制列舉法。

遞迴法是利用dfs的思想進行搜尋,檢索每一種方案進行容斥。

二進位制列舉的方法最大的好處是能夠枚舉出所有元素組合的不同集合。假設一個集合的元素有m個,則對於m長的二進

制數來說就有m個1或0的位置,對於每一個1就對應一個元素。

整個二進位制列舉完就是所有子集,從0到2^m就行。[0, 2^m)

以hdu 1796為例:

題意給定一個數n,數列m個數,求這小於n的數中,有多少個數滿足能被這m個數中任意一個數整除。

思路:1~n之間有多少個能被x整除的數,公式為n/x,題目中要求小於n,所以(n-1)/x。

可以遞迴法求,需要儲存中間重疊x次的最小公倍數lcm,符合題意的數有(n-1)/lcm個,利用

k表示重疊的次數進

行或者加上,或者減去。

也可以用二進位制列舉法,將符合條件的m個數,看作m位,每位是0或者是1,那麼一共有2^m種狀態,只要判斷一下每

一個狀態有多少個1,也就是有多少個數(重疊多少次),記為k,每一個1代表哪幾個具體的數,求這幾個數的最小

公倍數,然後(n-1)/lcm,  利用k的值來判斷應該減去還是加上即可。

遞迴版本:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int num[25];
int ans,tot,n,m;
void dfs(int pos,int pre_lcm,int k)
{
    for(int i=pos+1;i<tot;++i)
    {
        //int lcm=(num[i]*pre_lcm)/gcd(num[i],pre_lcm);
        int lcm = pre_lcm/__gcd(num[i], pre_lcm)*num[i];
        if(k&1) ans+=(n-1)/lcm;
        else ans-=(n-1)/lcm;
        dfs(i,lcm,k+1);
    }
}
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        ans=0,tot=1;
        memset(num,0,sizeof(num));
        for(int i=1;i<=m;++i)
        {
            int x;
            scanf("%d",&x);
            if(x>0&&x<n) num[tot++]=x;
        }
        dfs(0,1,1);
        printf("%d\n",ans);
    }
    return 0;
}

二進位制版本:
#include <bits/stdc++.h>
using namespace std;
int n, m, x, k, tot;
int up, t, pos, lcm, ans;
int num[25];
int main()
{
    while(~scanf("%d %d", &n, &m))
    {
        tot = 1; ans = 0;
        for(int i = 1; i <= m; ++i)
        {
            scanf("%d", &x);
            if(x > 0 && x < n) num[tot++] = x;
        }
         up = (1<<(tot-1));
        for(int i = 1; i < up; ++i)
        {
            t = i, k = 0, pos = 1; lcm = 1;
            while(t)
            {
                if(t&1)
                {
                    lcm = num[pos]/__gcd(lcm, num[pos])*lcm;
                    ++k;
                }
                t >>= 1; ++pos;
            }
            if(k&1) ans += (n-1)/lcm;
            else ans -= (n-1)/lcm;
        }
        printf("%d\n", ans);
    }
    return 0;
}


對於容斥原理我們可以利用數學歸納法證明: 證明:    時,等式成立(證明略)。 假設    時結論成立,則當    時, 所以當    時,結論仍成立。因此對任意    ,均可使所證等式成立。[1]

LL Q[100010],factor[110],num;
//Q陣列存放的就是右邊邊各項的因子數以及正負情況,factor[]存放對應物件的數目,num為有幾個物件
void Divid(LL n)  //n的素因子分解,得到每項素因子的個數
{
    num = 0;
    for(LL i = 2; i*i <= n; ++i)
    {
        if(n%i==0)
        {
            while(n%i==0)
                n /= i;
            factor[num++] = i;
        }
    }
    if(n != 1)
        factor[num++] = n;
}
LL solve(LL n)  //容斥定理,求
{
    LL k,t,ans;
    t = ans = 0;
    Q[t++] = -1;
    for(LL i = 0; i < num; ++i)
    {
        k = t;
        for(LL j = 0; j < k; ++j)
            Q[t++] = -1*Q[j]*factor[i];
    }
    //A∪B∪C = A+B+C - A∩B - B∩C - C∩A + A∩B∩C
    //Q陣列存放的就是A∪B∪C右邊邊各項的因子數以及正負情況。
    for(LL i = 1; i < t; ++i)
        ans += n/Q[i];
    //n/Q[i]累加起來就是A∪B∪C
    return ans;
}

例題:給定r,n求[1,r]內與n互素的個數有多少個?

思路:直接求解問題就是比較複雜的。所以我們還是研究這個問題是逆問題。也就是說求gcd(k,n) >= 2,在1 - n之間k有多少個 。那麼我們就可以列舉n的素因子來進行求解。

int solve(int r, int n) { vector<int>p; for (int i = 2; i * i <= n; ++i){ if (n % i == 0){ p.push_back(i); while (n % i == 0) n /= i; } } if (n > 1) p.push_back(n); int sum = 0; for (int S = 1; S < (1 << p.size()); ++S){ int mult = 1, bits = 0; for (int i = 0; i < p.size(); ++i){ if (S & (1 << i)){ ++bits; mult *= p[i]; } } int cur = r / mult; if (bits % 2 == 1) sum += cur; else sum -= cur; } return r - sum; }

相關推薦

原理模板+例題

網上找來方便自己看,理解。 容斥原理:在計數時,必須注意無一重複,無一遺漏。為了使重疊部分不被重複計算,人們研究出一種新的計數方法,這種方法的基本思想是:先不考慮重疊的情況,把包含於某內容中的所有物件

【專題總結】原理持續更新

從動機的角度出發。在用“做減法”的思想解決計數類問題時,可能會遇到“多減去符合條件的數目”,試圖加回來的時候又會遇到“多加上不符合條件的數目”的情況,這時候也許需要用容斥原理來設計計數演算法。 從實現的角度出發。在對事件集合的“並事件”計數遇到困難時,可通

原理二進位制列舉

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

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:對於整

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) 問

矩陣快速冪模板+例題

模板 #include<cstdio> #include<cmath>//pow函式,其實沒啥用 using namespace std; int n;long long k; const int N=pow(10,9)

HDU-4135 Co-prime原理模板

C - C Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Descrip

原理 —— 求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

【專題】計數問題排列組合,原理,卡特蘭數

spl 狀態 ans 補集 方便 常用 括號 inf 不存在 ---下面都是學習的筆記,還沒有整理,比較淩亂,有需自取吧。--- 【排列組合】 <加法原理>做一件事情有n個方法,第i個方法有pi種方案,則一共有p1+p2+...+pn種方案。 <乘法原理&

HDU 4390 Number Sequence 原理+組合計數

osi freopen ret dsm algo .cn iterator push_back man HDU 4390 題意: 大概就是這樣。不翻譯了:

【BZOJ1008】越獄排列組合計數,原理

code typedef ostream ima bzoj1008 image sca fin space 題意: 思路: 1 #include<cstdio> 2 #include<cstdlib> 3 #include<ios

BZOJ3771 TripleFFT+原理

ble algo pan double == max [] urn cstring   思路比較直觀。設A(x)=Σxai。先把只選一種的統計進去。然後考慮選兩種,這個直接A(x)自己卷起來就好了,要去掉選同一種的情況然後除以2。現在得到了選兩種的每種權值的方案數,再把這個

『ZOJ 3547』The Boss on Mars 原理

ostream idt employee ant mars pri mes because reason 傳送門戳這裏qwq 題目描述 On Mars, there is a huge company called ACM (A huge Company on M