1. 程式人生 > >【專題】計數問題(排列組合,容斥原理,卡特蘭數)

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

spl 狀態 ans 補集 方便 常用 括號 inf 不存在

---下面都是學習的筆記,還沒有整理,比較淩亂,有需自取吧。---

【排列組合】

<加法原理>做一件事情有n個方法,第i個方法有pi種方案,則一共有p1+p2+...+pn種方案。

<乘法原理>做一件事件有n個步驟,第i個步驟有pi種方案,則一共有p1p2...pn種方案。

乘法原理是加法原理的特殊情況,加法原理的關鍵是不重不漏地分類,若有重復可以考慮容斥原理。

<容斥原理>

|aUbUc|=|a|+|b|+|c|-|a∩b|-|b∩c|-|c∩a|+|a∩b∩c|

左邊是所有集合的並,右邊是若幹集合的交集搭配,根據搭配集合個數奇加偶減。

容斥的兩種形式:

1.(考慮滿足條件交的集合)加所有單條件集合,減所有雙條件交集合,加所有三條件交集合……

2.(考慮不滿足條件交的集合)加總集,減所有不滿足單條件集合,加所有不滿足[雙條件交]集合,加所有不滿足三條件交集合。

官方表述:記f(S)表示滿足集合S中至少一個條件的方案數g(S)表示滿足集合S中所有條件的方案數記h(S)表示不滿足集合S中所有條件的方案數

f(S)=Σ(-1)|T|-1g(T),其中T為S的非空子集

g(S)=Σ(-1)|T|h(T),其中T為S的子集。(不滿足空子集的集合就是總集)

實際上,容斥原理就是已知g(s)或h(S),求解f(S)。也就是已知所有集合交(或非集合交),就可以用容斥原理求解集合並

如果考慮非集合交不方便,也可以用補集思想轉化為總集-外集,其中外集就可以用另一種集合交求解了。

容斥後就不存在重復了。

例題:

1.對一個n*m的棋盤染色,每格可以是黑色或白色,要求每行每列都有黑格。

用容斥原理轉化為枚舉其中一些條件不滿足,也就是有一些行一些列全是白格。(也可以視為用總集減去一行或一列全白格的方案數)

枚舉行數i和列數j,那麽選出i行j列的方案數是C(n,i)C(m,j),這i行j列全是白格的方案數是2(n-i)(m-j)

所以答案就是Σ(-1)i+jC(n,i)C(m,j)2(n-i)(m-j)。i,j>=0

時間復雜度O(nm)。

2.對一個n*m的棋盤染色,每格可以是黑色或白色,要求存在一行或一列全是黑格。

其實就是上一題的外集。

ans=Σ(-1)i+j-1

C(n,i)C(m,j)2(n-i)(m-j)。(註意這是二維容斥,設置好初狀態(0,1)和(1,0)為+,然後順次容斥就可以了)。

3.對一個n*m的棋盤染色,每格可以是黑色或白色,要求存在一行和一列全是黑格。

需要滿足的條件是:(A)存在一行都是黑格。(B)存在一列都是黑格。

先對A用容斥原理,答案變為Σ(-1)i-1C(n,i)*(有i行都是黑格且滿足B的方案數)。

再對B用容斥原理,答案變為Σ(-1)i-1C(n,i)*Σ(-1)j-1C(m,j)*(有i行j列都是黑格的方案數)。

化簡一下就是Σ(-1)i+jC(n,i)C(m,j)2(n-i)(m-j),其中i,j>=1。

時間復雜度O(nm)。

題目要求滿足其中一個條件的方案數,就容斥。要求滿足其中所有條件,就總集-外集

<抽屜原理>……

<排列>A(n,m)表示在n個數中選m個的排列數(n為下標,m為上標)

由乘法原理,每個步驟選擇一個數,則分別有n,n-1,...n-m+1種選擇,則可以簡單地表示為:

A(n,m)=n*(n-1)*...*(n-m+1)即

A(n,m)=n!/(n-m)!

特別地,n個數的全排列是A(n,n)=n!

<組合>C(n,m)表示在n個數中選m個的組合數(n為下標,m為上標)

首先選出m個數的組合,然後把這m個數進行全排列。由乘法原理知A(n,m)=C(n,m)*A(m,m),即C(n,m)=A(n,m)/A(m,m)=n!/((n-m)!m!)。

C(n,m)=n!/((n-m)!m!)

<組合數性質>

1.C(n,0)=C(n,n)=1

2.核心性質一:C(n,k)=C(n,n-k)  組合的對稱性!

3.核心性質二:C(n+1,k+1)=C(n,k)+C(n,k+1)  組合數的遞推公式(楊輝三角)。

證明:n+1個數裏面選擇k+1個數有兩類方法,若選擇第一個數則轉化為C(n,k),否則轉化為C(n,k+1)。

4.C(n,k+1)=C(n,k)*(n-k)/(k+1)  組合數同下標遞推公式

  直接利用公式變換:

    C(n,k+1)=n(n-1)...(n-k+1)(n-k)/(k+1)!

    C(n,k)=n(n-1)...(n-k+1)/k!

  兩式相除得C(n,k+1)/C(n,k)=(n-k)/(k+1)

此性質可用於二項式定理(後面介紹)的預處理過程。

性質:ΣC(n,0~n)=2^n

5.重復元素問題:排列組合往往不是簡單的C和A公式一寫就可以解決的,因為有大量的重復元素,需要做大量去重工作。

  (1)有重復元素的全排列。

    有k個元素,其中第i個元素有ni個,求全排列個數。

    令總數為n,設答案為x。對於答案中的每個排列,對相同元素標號後,相同元素內部可以再進行全排列,所以:

    n1!n2!n3!...nk!x=n!得到x=n!/(n1!n2!n3!...nk!)

    這裏和組合數的推導一樣,從而得到結論:加上m個數的標號就是乘m個數的全排列,去除m個數的標號就是除以m個數的全排列

  (2)★可重復選擇的組合

    問題:n個數中取k個,每個數可以取多次,求組合

     試著反過來考慮,把k個1劃分成n段的方案數。

     問題轉化為求解x1+x2+...+xn=k的非負整數解個數,由於0很麻煩,所以令xi=xi+1,則轉化為:

     求解x1+x2+...+xn=k+n的正整數解的個數。

     想象有k+n個“1”排成一排,則問題等價於把這些“1”分成n個部分的方法數。

     隔板法:重復問題中常轉化為x1+x2...+xn=X的形式,然後可以想象為在X-1個間隔中放置n-1個隔板。當隔出區間可能為0時,整體+1。

     那麽在k+n-1個間隔中選n-1個,即ans=C(k+n-1,n-1)=C(n+k-1,k)。

6.正難則反的思想(補集思想),在這類數學問題中尤其常用,而且往往十分隱蔽、不易發現

技術分享圖片
#include<cstdio>
#include<cstring>
#include<cctype>
#include<cmath>
#include<algorithm>
#define ll long long
using namespace std;
int read(){
    char c;int s=0,t=1;
    while(!isdigit(c=getchar()))if(c==-)t=-1;
    do{s=s*10+c-0;}while(isdigit(c=getchar()));
    return s*t;
}
int min(int a,int b){return a<b?a:b;}
int max(int a,int b){return a<b?b:a;}
int abs(int x){return x>0?x:-x;}
void mins(int &a,int b){if(a>b)a=b;}
void maxs(int &a,int b){if(a<b)a=b;}
//void insert(int u,int v){tot++;e[tot].v=v;e[tot].from=first[u];first[u]=tot;}
/*------------------------------------------------------------*/
const int inf=0x3f3f3f3f,MOD=1000000007;
ll n;

void gcd(ll a,ll b,ll& d,ll& x,ll& y){
    if(!b){d=a;x=1;y=0;}
     else{gcd(b,a%b,d,y,x);y-=x*(a/b);}
}
ll inv(ll a,ll n){
    ll d,x,y;
    gcd(a,n,d,x,y);
    return (x%n+n)%n;
}
ll fac[1010],fav[1010];
ll C(ll n,ll m){return fac[n]*fav[m]%MOD*fav[n-m]%MOD;}
int main(){
    fac[0]=1;fav[0]=1;
    for(int i=1;i<=1001;i++)fac[i]=fac[i-1]*i%MOD;
    for(int i=1;i<=1001;i++)fav[i]=inv(fac[i],MOD);
    
    return 0;
}
View Code

例題:

1.n個數字的和不超過m的的方案數。

若是等於m,則求C(n+m-1,n-1)即(n+m-1,m)。

轉化為求ΣC(n+i-1,i),由組合數遞推公式C(n,m)=C(n-1,m-1)+C(n-1,m)得

C(n-1,0)+C(n,1)+C(n+1,2)+C(n+2,3)+...+C(n+m-1,m)

=C(n,0)+C(n,1)+C(n+1,2)+C(n+2,3)+...+C(n+m-1,m)

=C(n+m,m)  兩兩一步一步湊剩一個數字。

2.袋球模型

①n個不同的球和m個不同的袋子

每個球都有m中選擇,ans=m^n。

②n個相同的球和m個不同的袋子

視為將n+m個‘1‘分成m段,所以ans=C(n+m-1,m-1)。

③n個不同的球和m個相同的袋子

f[i][j]表示i球放j袋且袋子不空的方案數。

考慮一個球,可以放到j-1袋中的任意一個,也可以單獨放在新的j袋中。(球不同,所以可以依次考慮每個球)

f[i][j]=f[i-1][j-1]+f[i-1][j]*j。

④n個相同的球和m個相同的袋子

強制規定袋子中的球數單調不減,則放球需要放後綴和的形式,“1 1 2”應視為[1,3]+1和[3,3]+1。

考慮當前有兩種選擇,新建一個空袋子作為第一個(0)或在第一個袋子放球(後綴整體+1)。

f[i][j]=f[i-j][j]+f[i][j-1]

這樣轉移的正確性,可以考慮一種已有的方案,一定是通過依次增加袋子和放球來達到的,這樣就可以保證不重不漏。(類似完全背包)

<二項式定理>

觀察楊輝三角可以發現,它的第i行的數字是C(i-1,0),C(i-1,1)...C(i-1,i-1),即同一行同下標,上標從0開始遞增。

由楊輝三角也可以發現組合數的兩大核心性質(同行左右對稱,每個數等於上面和上面左邊兩數之和)。

另一方面,把(a+b)^n展開的多項式系數和楊輝三角一致,由此可得

★二項式定理:技術分享圖片

其中組合數對應楊輝三角也對應系數,後邊對應項的內容。

也可以理解為n個括號,多少個選a,多少個選b出來的結果。

而求解系數可以使用組合數性質4。

【卡特蘭數】Catalan

引用自:卡特蘭數 — 計數的映射方法的偉大勝利

Catalan數列:1 1 1 2 5 14 42 132 429 1430 4862 16796

計數問題首選排列組合,其中要考慮可能暗含的斐波那契數列和卡特蘭數列。

計數問題可以考慮遞推形式,即把f(x)納入公式計算範圍,考慮f(i)和f(i-1)的關系等,多轉換方便遞推的角度就不會束手無策。

對於一類情況,只著重關註一點。

1.分治思想

一個問題A,規模為n,可以用分治的思想,先固定一個元素,然後將剩下n-1個元素拆分成兩個問題,根據固定的元素位置不同,兩個問題分別是(0,n-1)(1,n-2)...(n-1,0)。

對於三角剖分數問題,先固定三角形V1VnVk,根據乘法原理,包含這個三角形的有f(k)f(n-k+1)種方案。根據加法原理,f(n)=f(2)f(n-1)+f(3)f(n-2)...+f(n-1)f(2)。

對於二叉樹已知葉子的形態計數問題,也可以固定根後遞推。

對於出棧入棧序問題,假設第k個數最後出棧,則1~k-1有完整的出入棧,k+1~n同。

所以卡特蘭數是利用分治思想遞推的典例。看待的卡特蘭數觀點中,分治思想是主流,也更容易理解。

2.計數映射思想

將難以統計的數映射為可以統計的數,這就是計數映射思想。

卡特蘭數的計數映射思想首先體現在出棧入棧序

n個數字,有多少種合法的出入序列?n=3時的合法序列之一:+1,-1,+1,+1,-1,-1

對於n個數字,就是要在2n個1中添加n個“+”,則序列總數C(2n,n)。

但是,未入棧先出棧顯然不合法,但難以統計,這時考慮將不合法情況統計出來並映射。

對於一種不合法情況,我們只關註它最左邊第一次前綴和為-1的情況,此時實際上是前2x+1個1中選擇了x個”+“,x+1個”-“的情況。。

此時統計仍然不便,我們將這異常的2x-1個符號翻轉,使異常體現在整個數列,那麽這個數列就有n+1個“+”,n-1個“-”。

每一種2n個1中選擇n+1個加號的情況,將其第一次前綴和為+1時的2x+1個符號翻轉,就對應了一種不合法情況。

那麽不合法情況的總數就是2n個1中添加n+1個“+”的序列總數C(2n,n+1)或C(2n,n-1)。將難以統計的情況轉化為可以統計的情況

C(2n,n)包含了不合法情況,我們只能統計包含不合法情況的數列(即有可能前綴和<0)。

轉化後的C(2n,n-1)同樣包含了不合法情況,但是沒關系,前綴和<0的翻轉後就>0合法,未翻轉的不合法也無所謂,前綴和>0第一個位置翻轉後正是我們要找的不合法位置。

所以得到卡特蘭數列f(n)=C(2n,n)-C(2n,n-1)=1/(n+1)*C(2n,n)

例子1:n葉子的滿二叉樹形態數:對於每一形態從根節點遍歷,向左+1,向右-1,轉化為出棧入棧序。

例子2:幾何模型——計數映射的擴展。幾何模型是計數映射理解的最大優勢。

考慮n*n的方格,從左下到右上不跨越對角線的路徑數,向右記為+1,向上記為-1,跨越對角線就是向上多於向右,則轉化為卡特蘭數。

考慮n*m的方格,用出棧入棧序的方法考慮,一條不合法路徑只關註它第一次跨越對角線,將那處位置及其前面的路徑翻轉(上變左,左變上),那麽就變成了到(n-1,m+1)的路徑數。

那麽此時計算f(n,m)=C(n+m,n)-C(n+m,n-1)。當n=m時就是Catalan數。

例子3:圓上連弦,出弦+1,入弦-1,圓上點順序掃描即為入棧出棧序。

例子4:合法括號表達式=連乘方法數=入棧出棧序

★總結:Catalan數的題目,一般只有從幾種方式看出:轉化為入棧出棧序,轉化為平面幾何不跨線路徑數,定點分治,打表。

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