acm-排列組合學習筆記(更新中)

&*^*&(發表於2020-11-04

引言

本文主要介紹排列與組合的相關知識點,以及重要的一些結論推論及其證明,會給出少量的例題,此外本文是建立在作者的需求上,故更多簡單的內容不會涉及,預設讀者已經擁有前置技能,本文還在更新中。。。

一、集合

前置規定:

  1. 0 ! = 1 0!=1 0!=1
  2. m > n m>n m>n時有 C n m = A n m = 0 C_n^m=A_n^m=0 Cnm=Anm=0

1.不可重集

(1).普通排列

從有 n n n個元素的不可重集中選則 k k k個元素排成一個序列的方案記為 A n k A_n^k Ank
其中 A n k = n ( n − 1 ) ( n − 2 ) . . . . ( n − k + 1 ) = n ! ( n − k ) ! A_n^k=n(n-1)(n-2)....(n-k+1)={n!\over (n-k)!} Ank=n(n1)(n2)....(nk+1)=(nk)!n!

證明:考慮從集合中一個一個拿出元素,第一次有 n n n種選擇,第二次有 n − 1 n-1 n1種選擇,…,第 k k k次有 n − k + 1 n-k+1 nk+1種選擇,根據乘法原理我們有 A n k = n ( n − 1 ) ( n − 2 ) . . . ( n − k + 1 ) A_{n}^k=n(n-1)(n-2)...(n-k+1) Ank=n(n1)(n2)...(nk+1)

(2).圓排列

從有 n n n個元素的不可重集中選擇 k k k個元素排成一個環的總方案數為 A n k k = n ! k ( n − k ) ! {A_n^k\over k}={n!\over k(n-k)!} kAnk=k(nk)!n!

證明:如果想要構成環,我們可以先從集合中取 k k k個元素排成一個序列,然後再把序列首位相接即可,對於一個圓排列而言,如果把它拆成一個序列有 k k k種拆法,對應是 k k k個不同的序列,因此序列的排列數是圓的排列數的 k k k倍,故命題正確。

(3).組合

n n n個元素的不可重集中選擇 k k k個元素的方案數記為 C n k C_n^k Cnk(現在更記作 ( n k ) \binom{n}{k} (kn),讀作 n n n k k k ),其中 C n k = A n k k ! = n ! k ! ( n − k ) ! C_n^k={A_n^k\over k!}={n!\over k!(n-k)!} Cnk=k!Ank=k!(nk)!n!

證明:從不可重集中選擇 k k k個元素的方式可以拆分為先從集合中選擇 k k k個元素構成一個排列,方案數有 A n k A_n^k Ank,但是同一個選擇元素的方案會對應有 k ! k! k!種排列,故選擇的方案為 A n k k ! 。 {A_n^k\over k!}。 k!Ank

2.可重集

(1).排列

[1].無限集

從含有 n n n種元素的無限可重集(每種元素都有無限個)中選出 k k k個元素構成排列的方案數為 n k n^k nk

證明:取元素的過程中每次都有 n n n種選擇,故總共有 n ⋅ n ⋯ n ( k 個 n ) = n k n\cdot n\cdots n(k個n)=n^k nnn(kn)=nk種方案。

推廣:如果每種元素的個數至少為 k k k則命題依然成立。

[2].有限集

假設 k k k種元素的有限集中的元素的個數分別為 n 1 , n 2 , . . . , n k n_1,n_2,...,n_k n1,n2,...,nk,且 n = n 1 + n 2 + . . . + n k n=n_1+n_2+...+n_k n=n1+n2+...+nk,則該集合的全排列數記作 ( n n 1 , n 2 , . . . , n k ) \binom{n}{n_1,n_2,...,n_k} (n1,n2,...,nkn),它實際上等於 n ! n 1 ! n 2 ! . . . n k ! {n!\over n_1!n_2!...n_k!} n1!n2!...nk!n!

證明:如果這是 n n n個不同的元素那麼全排列就等於 n ! n! n!,但是裡面存在重複的元素,因此需要去重,對於同一個排列而言第 i i i種元素會重複 n i ! n_i! ni!次,因此需要除去,故原命題成立。

(2).組合

[1].無限集

n n n種元素的無限集(每種元素無限個)中選擇 k k k個元素的方案數為 ( n + k − 1 k ) = ( n + k − 1 n − 1 ) \binom{n+k-1}{k}=\binom{n+k-1}{n-1} (kn+k1)=(n1n+k1)

證明:假設第 i i i種元素選擇 x i x_i xi個,於是有 x 1 + x 2 + . . . + x n = k x_1+x_2+...+x_n=k x1+x2+...+xn=k,其中 x i ≥ 0 , ∀ i ∈ [ 1 , n ] x_i\ge 0,\forall i\in[1,n] xi0,i[1,n]。考慮令 y i = x i + 1 , ∀ i ∈ [ 1 , n ] y_i=x_i+1,\forall i\in[1,n] yi=xi+1,i[1,n],於是有 y 1 + y 2 + . . . + y n = n + k y_1+y_2+...+y_n=n+k y1+y2+...+yn=n+k,然後思考這些 y y y的取值組合有多少種,我們可以把它轉化為隔板模型:有 n + k n+k n+k個小球放在盒子裡,然後要在這些小球之間的 n + k − 1 n+k-1 n+k1個縫隙中插入 n − 1 n-1 n1個隔板,因此有 ( n + k − 1 n − 1 ) \binom{n+k-1}{n-1} (n1n+k1)種方案數。

[2].有限集

假設 k k k種元素的有限集中的元素的個數分別為 n 1 , n 2 , . . . , n k n_1,n_2,...,n_k n1,n2,...,nk,且 n = n 1 + n 2 + . . . + n k n=n_1+n_2+...+n_k n=n1+n2+...+nk,則從該集合中選擇 r r r個元素的方案數為 ∑ i = 0 k ( − 1 ) k ∑ ∣ A ∣ = i ( k + r − ∑ j = 1 i n A j − i − 1 k − 1 ) \sum_{i=0}^k(-1)^k\sum_{|A|=i}\tbinom{k+r-\sum_{j=1}^{i}n_{A_j}-i-1}{k-1} i=0k(1)kA=i(k1k+rj=1inAji1),其中 A A A代表 { 1 , 2 , . . . , k } \{1,2,...,k\} {1,2,...,k}的子集, A j A_j Aj代表集合 A A A的第 j j j個元素。

證明:考慮容斥原理(見第六部分),設第 i i i種元素選擇 x i x_i xi個,則滿足 x 1 + x 2 + . . . + x k = r , ∀ i ∈ [ 1 , k ] , x i ≤ n i x_1+x_2+...+x_k=r,\forall i\in[1,k],x_i\le n_i x1+x2+...+xk=r,i[1,k],xini,在保證 x i ≥ 0 , ∀ i ∈ [ 1 , k ] x_i\ge 0,\forall i\in[1,k] xi0,i[1,k]的前提條件下我們不妨設我們要求解的方案數 ∣ S ∣ = ∣ { x 1 ≤ n 1 ∧ x 2 ≤ n 2 ∧ . . . ∧ x k ≤ n k ∧ ∑ i = 1 k x i = r } ∣ |S|=|\{x_1\le n_1 \land x_2 \le n_2\land ...\land x_k\le n_k \land \sum_{i=1}^kx_i=r\}| S={x1n1x2n2...xknki=1kxi=r},設方案 S i = { x i ≤ n i ∧ ∑ j = 1 k x j = r } , S i ‾ = { x i ≥ n i + 1 ∧ ∑ j = 1 k x j = r } } S_i=\{x_i\le n_i\land \sum_{j=1}^kx_j=r\},\overline{S_i}=\{x_i\ge n_i+1\land \sum_{j=1}^kx_j=r\}\} Si={xinij=1kxj=r},Si={xini+1j=1kxj=r}},設全集 U = { ∑ i = 1 k x i = r } U=\{\sum_{i=1}^kx_i=r\} U={i=1kxi=r},那麼顯然有 ∣ S ∣ = ∣ U ∣ − ∣ ∪ i = 1 k S i ‾ ∣ |S|=|U|-|\cup_{i=1}^k\overline{S_i}| S=Ui=1kSi,現在考慮如何求解 ∣ ∪ i = 1 k S i ‾ ∣ |\cup_{i=1}^k\overline{S_i}| i=1kSi
根據前置定理 ∣ S i ‾ ∣ = ( k + r − n i − 2 k − 1 ) |\overline{S_i}|=\tbinom{k+r-n_i-2}{k-1} Si=(k1k+rni2)(參考第一部分2.(2).[1])
我們有如下等式成立:
∣ ∪ i = 1 k S i ‾ ∣ = ∑ i = 1 k ∣ S i ‾ ∣ − ∑ 1 ≤ i 1 < i 2 ≤ k ∣ S i 1 ‾ ∩ S i 2 ‾ ∣ + ∑ 1 ≤ i 1 < i 2 < i 3 ≤ k ∣ S i 1 ‾ ∩ S i 2 ‾ ∩ S i 3 ‾ ∣ + . . . + ( − 1 ) k − 1 ∣ ∩ i = 1 k S i ‾ ∣ = ∑ i = 1 k ( k + r − n i − 2 k − 1 ) − ∑ 1 ≤ i 1 < i 2 ≤ k ( k + r − n i 1 − n i 2 − 3 k − 1 ) + . . . + ( − 1 ) k − 1 ( k + r − ∑ i = 1 k n i − k − 1 k − 1 ) = ∑ i = 1 k ( − 1 ) i − 1 ∑ ∣ A ∣ = i ( k + r − ∑ j = 1 i n A j − i − 1 k − 1 ) \begin{aligned} |\cup_{i=1}^k\overline{S_i}|&=\sum_{i=1}^k |\overline{S_i}|-\sum_{1\le i_1<i_2\le k}|\overline{S_{i_1}}\cap\overline{S_{i_2}}|+\sum_{1\le i_1<i_2<i_3\le k}|\overline{S_{i_1}}\cap\overline{S_{i_2}}\cap \overline{S_{i_3}}|+...+(-1)^{k-1}|\cap_{i=1}^k\overline{S_i}|\\ &=\sum_{i=1}^k\tbinom{k+r-n_i-2}{k-1}-\sum_{1\le i_1<i_2\le k}\tbinom{k+r-n_{i_1}-n_{i_2}-3}{k-1}+...+(-1)^{k-1}\tbinom{k+r-\sum_{i=1}^kn_i-k-1}{k-1}\\ &=\sum_{i=1}^k(-1)^{i-1}\sum_{|A|=i}\tbinom{k+r-\sum_{j=1}^in_{A_j}-i-1}{k-1} \end{aligned} i=1kSi=i=1kSi1i1<i2kSi1Si2+1i1<i2<i3kSi1Si2Si3+...+(1)k1i=1kSi=i=1k(k1k+rni2)1i1<i2k(k1k+rni1ni23)+...+(1)k1(k1k+ri=1knik1)=i=1k(1)i1A=i(k1k+rj=1inAji1)
於是我們有 ∣ S ∣ = ∣ U ∣ − ∣ ∪ i = 1 k S i ‾ ∣ = ∑ i = 0 k ( − 1 ) k ∑ ∣ A ∣ = i ( k + r − ∑ j = 1 i n A j − i − 1 k − 1 ) |S|=|U|-|\cup_{i=1}^k\overline{S_i}|=\sum_{i=0}^k(-1)^k\sum_{|A|=i}\tbinom{k+r-\sum_{j=1}^{i}n_{A_j}-i-1}{k-1} S=Ui=1kSi=i=0k(1)kA=i(k1k+rj=1inAji1)成立。

二、組合數(二項式係數)

1.二項式定理

(1).基本內容

( a + b ) n = ∑ i = 0 n C n i a i b n − i = ∑ i = 0 n ( n i ) a i b n − i , 其 ∑ i = 0 n ( n i ) = 2 n (a+b)^n=\sum_{i=0}^nC_n^ia^ib^{n-i}=\sum_{i=0}^n\tbinom{n}{i}a^ib^{n-i},其\sum_{i=0}^n\tbinom{n}{i}=2^n (a+b)n=i=0nCniaibni=i=0n(in)aibnii=0n(in)=2n

證明:由於 ( a + b ) n = ( a + b ) ( a + b ) . . . ( a + b ) , ( n 個 ( a + b ) ) (a+b)^n=(a+b)(a+b)...(a+b),(n個(a+b)) (a+b)n=(a+b)(a+b)...(a+b),(n(a+b)),根據展開式的展開過程我們知道只需要從每個 ( a + b ) (a+b) (a+b)中選擇一個 a a a b b b然後乘起來就得到了最終的結果,因此我們有每一項 a x b y a^xb^y axby滿足 x + y = n x+y=n x+y=n,考慮有多少個 a i b n − i a^ib^{n-i} aibni,顯然我們只需要從 n n n ( a + b ) (a+b) (a+b)中選出 i i i a a a,剩下的即是 b b b,故總方案數為 C n i C_n^i Cni,故 ( a + b ) n = ∑ i = 0 n C n i a i b n − i (a+b)^n=\sum_{i=0}^nC_n^ia^ib^{n-i} (a+b)n=i=0nCniaibni

還可以利用歸納法證明,用公式 C n i − 1 + C n i = C n + 1 i C_n^{i-1}+C_n^i=C_{n+1}^i Cni1+Cni=Cn+1i來證明(比較簡單就不證了)。

此外如果我們令 a = 1 , b = 1 a=1,b=1 a=1,b=1就能得到 ∑ i = 0 n ( n i ) = 2 n \sum_{i=0}^n\tbinom{n}{i}=2^n i=0n(in)=2n

(2).推廣

二項式定理可以推廣為多項式形式:
( ∑ i = 1 k x i ) n = ∑ n 1 + n 2 + . . . + n k = n ( n n 1 , n 2 , . . . , n k ) x 1 n 1 x 2 n 2 . . . x k n k , 其 中 ∑ n 1 + n 2 + . . . + n k = n ( n n 1 , n 2 , . . . , n k ) = k n (\sum_{i=1}^kx_i)^n=\sum_{n_1+n_2+...+n_k=n}\tbinom{n}{n_1,n_2,...,n_k}x_1^{n_1}x_2^{n_2}...x_k^{n_k},其中\sum_{n_1+n_2+...+n_k=n}\tbinom{n}{n_1,n_2,...,n_k}=k^n (i=1kxi)n=n1+n2+...+nk=n(n1,n2,...,nkn)x1n1x2n2...xknk,n1+n2+...+nk=n(n1,n2,...,nkn)=kn

證明:實際上二項式定理可以寫成 ( ∑ i = 1 2 x i ) n = ∑ n 1 + n 2 = n ( n n 1 , n 2 ) x 1 n 1 x 2 n 2 (\sum_{i=1}^2x_i)^n=\sum_{n_1+n_2=n}\tbinom{n}{n_1,n_2}x_1^{n_1}x_2^{n_2} (i=12xi)n=n1+n2=n(n1,n2n)x1n1x2n2,類比過去即可。不過要嚴謹證明的話也是同樣的兩種方式:
一是考慮它的組合學意義,我們首先確定 x 1 , x 2 , . . . , x k x_1,x_2,...,x_k x1,x2,...,xk它們的冪次,也就是 n 1 , n 2 , . . . , n k n_1,n_2,...,n_k n1,n2,...,nk,要得到它們這意味著我們要對每個括號(即 ( x 1 + x 2 + . . . x k ) (x_1+x_2+...x_k) (x1+x2+...xk))選擇一個 x i , i = 1 , 2 , . . . , k x_i,i=1,2,...,k xi,i=1,2,...,k,這相當於我們對 n 1 ∗ x 1 , n 2 ∗ x 2 , . . . , n k ∗ x k n_1*x_1,n_2*x_2,...,n_k*x_k n1x1,n2x2,...,nkxk( n i ∗ x i n_i*x_i nixi代表 n i n_i ni x i x_i xi),進行排序,然後按順序對映到每個括號,那麼有多少種排序方式呢?根據一.2.(1).[2]我們知道有限可重集的全排列為 ( n n 1 , n 2 , . . . , n k ) \tbinom{n}{n_1,n_2,...,n_k} (n1,n2,...,nkn),故命題成立。

二是數學歸納法,這裡不做證明,感興趣的可以去網上搜尋相關資料。

此外如果我們令 x i = 1 , ∀ i ∈ [ 1 , k ] x_i=1,\forall i\in[1,k] xi=1,i[1,k]就能得到 ∑ n 1 + n 2 + . . . + n k = n ( n n 1 , n 2 , . . . , n k ) = k n \sum_{n_1+n_2+...+n_k=n}\tbinom{n}{n_1,n_2,...,n_k}=k^n n1+n2+...+nk=n(n1,n2,...,nkn)=kn

2. 帕斯卡三角形

帕斯卡三角形如下表所示:

n/k012345678
01
111
2121
31331
414641
515101051
61615201561
7172135352171
818285670562881

其中第 n n n行第 k k k列代表的數字為 C n k C_n^k Cnk
容易發現帕斯卡三角形的幾條性質:

  1. C n k = C n − 1 k − 1 + C n − 1 k C_n^k=C_{n-1}^{k-1}+C_{n-1}^k Cnk=Cn1k1+Cn1k,從表上容易看出
  2. 對稱關係 C n k = C n n − k C_n^k=C_n^{n-k} Cnk=Cnnk
  3. 假設從 ( 0 , 0 ) (0,0) (0,0)出發只能向下或向右下走,那麼到達 ( n , k ) (n,k) (n,k)的路徑數為 C n k C_n^{k} Cnk
  4. C n i , i ∈ [ 0 , n ] C_n^i,i\in[0,n] Cni,i[0,n]具有單峰性,即當 n n n為偶數有 C n 0 < C n 1 < C n 2 < . . . < C n n 2 − 1 < C n n 2 > C n n 2 + 1 > . . . > C n n − 2 > C n n − 1 > C n n C_n^0<C_n^1<C_n^2<...<C_n^{\frac n2-1}<C_n^{\frac n2}>C_n^{\frac n2+1}>...>C_n^{n-2}>C_n^{n-1}>C_n^{n} Cn0<Cn1<Cn2<...<Cn2n1<Cn2n>Cn2n+1>...>Cnn2>Cnn1>Cnn;當 n n n為奇數有 C n 0 < C n 1 < C n 2 < . . . < C n n − 1 2 = C n n + 1 2 > . . . > C n n − 2 > C n n − 1 > C n n C_n^0<C_n^1<C_n^2<...<C_n^{\frac{n-1}2}=C_{n}^{\frac {n+1}2}>...>C_{n}^{n-2}>C_n^{n-1}>C_n^n Cn0<Cn1<Cn2<...<Cn2n1=Cn2n+1>...>Cnn2>Cnn1>Cnn成立。

3.組合數性質

  1. k ( n k ) = n ( n − 1 k − 1 ) k\binom{n}{k}=n\binom{n-1}{k-1} k(kn)=n(k1n1),也常寫作 ( n k ) = n k ( n − 1 k − 1 ) \tbinom{n}k=\frac nk\tbinom{n-1}{k-1} (kn)=kn(k1n1)
    證明: k ( n k ) = k n ! k ! ( n − k ) ! = n ( n − 1 ) ! ( k − 1 ) ! ( n − 1 − ( k − 1 ) ) ! = n ( n − 1 k − 1 ) k\binom{n}{k}=k\frac{n!}{k!(n-k)!}=n\frac{(n-1)!}{(k-1)!(n-1-(k-1))!}=n\binom{n-1}{k-1} k(kn)=kk!(nk)!n!=n(k1)!(n1(k1))!(n1)!=n(k1n1)

  2. ( n k ) = ( n n − k ) \binom{n}{k}=\binom{n}{n-k} (kn)=(nkn)
    證明: ( n k ) = n ! k ! ( n − k ) ! = n ! ( n − k ) ! ( n − ( n − k ) ) ! = ( n n − k ) \binom{n}{k}=\frac{n!}{k!(n-k)!}=\frac{n!}{(n-k)!(n-(n-k))!}=\binom{n}{n-k} (kn)=k!(nk)!n!=(nk)!(n(nk))!n!=(nkn)

  3. ∑ i = 0 n ( n i ) = 2 n \sum_{i=0}^n\binom{n}{i}=2^n i=0n(in)=2n
    證明:
    方法一:考慮牛頓二項式有 ( 1 + 1 ) n = ∑ i = 0 n ( n i ) 1 i 1 n − i ⇒ ∑ i = 0 n ( n i ) = 2 n (1+1)^n=\sum_{i=0}^n\binom{n}{i}1^i1^{n-i}\Rightarrow \sum_{i=0}^n\binom{n}{i}=2^n (1+1)n=i=0n(in)1i1nii=0n(in)=2n
    方法二:考慮n個元素的集合的子集個數顯然是 2 n 2^n 2n個,從另一個角度來說,我們可以把它的子集按照元素的個數進行劃分,對於含有0個元素的子集只有 ( n 0 ) = 1 \tbinom{n}0=1 (0n)=1個,含有1個元素的子集有 ( n 1 ) = n \tbinom{n}1=n (1n)=n個…,因此子集總個數為 ∑ i = 0 ( n i ) = 2 n \sum_{i=0}\binom{n}i=2^n i=0(in)=2n個。

  4. ∑ i = 0 n ( − 1 ) i ( n i ) = 0 \sum_{i=0}^n(-1)^i\binom{n}{i}=0 i=0n(1)i(in)=0
    證明:考慮牛頓二項式 ( 1 − 1 ) n = ∑ i = 0 n ( n i ) ( − 1 ) i 1 n − i ⇒ ∑ i = 0 n ( − 1 ) i ( n i ) = 0 (1-1)^n=\sum_{i=0}^n\binom{n}{i}(-1)^i1^{n-i}\Rightarrow \sum_{i=0}^n(-1)^i\binom{n}{i}=0 (11)n=i=0n(in)(1)i1nii=0n(1)i(in)=0

  5. ( n 0 ) + ( n 2 ) + ( n 4 ) + . . . = ( n 1 ) + ( n 3 ) + ( n 5 ) + . . . = 2 n − 1 \binom{n}{0}+\binom{n}{2}+\binom{n}{4}+...=\binom{n}{1}+\binom{n}{3}+\binom{n}{5}+...=2^{n-1} (0n)+(2n)+(4n)+...=(1n)+(3n)+(5n)+...=2n1
    證明:由4可知 ∑ i = 0 n ( n i ) ( − 1 ) i = 0 \sum_{i=0}^n\binom{n}{i}(-1)^i=0 i=0n(in)(1)i=0,因此有奇數項和與偶數項和相等。

  6. ∑ i = 0 n i ( n i ) = n 2 n − 1 \sum_{i=0}^ni\binom{n}i=n2^{n-1} i=0ni(in)=n2n1
    證明:由於1可知 i ( n i ) = n ( n − 1 i − 1 ) i\binom{n}{i}=n\binom{n-1}{i-1} i(in)=n(i1n1),因此有:
    ∑ i = 0 n i ( n i ) = ∑ i = 1 n i ( n i ) = ∑ i = 1 n n ( n − 1 i − 1 ) = n ∑ i = 0 n − 1 ( n − 1 i ) = n 2 n − 1 \begin{aligned} \sum_{i=0}^ni\tbinom{n}{i}&=\sum_{i=1}^ni\tbinom{n}i\\ &=\sum_{i=1}^nn\tbinom {n-1}{i-1}\\ &=n\sum_{i=0}^{n-1}\tbinom{n-1}i\\ &=n2^{n-1} \end{aligned} i=0ni(in)=i=1ni(in)=i=1nn(i1n1)=ni=0n1(in1)=n2n1

  7. ∑ i = 0 n i 2 ( n i ) = n ( n + 1 ) 2 n − 2 \sum_{i=0}^ni^2\tbinom{n}{i}=n(n+1)2^{n-2} i=0ni2(in)=n(n+1)2n2
    證明:類似於6的證明。
    ∑ i = 0 n i 2 ( n i ) = ∑ i = 1 n i n ( n − 1 i − 1 ) = n ∑ i = 0 n − 1 ( i + 1 ) ( n − 1 i ) = n ( ∑ i = 0 n − 1 i ( n − 1 i ) + ∑ i = 0 n − 1 ( n − 1 i ) ) = n [ ( n − 1 ) 2 n − 2 + 2 n − 1 ] = n ( n + 1 ) 2 n − 2 \begin{aligned} \sum_{i=0}^ni^2\tbinom{n}{i}&=\sum_{i=1}^nin\tbinom{n-1}{i-1}\\ &=n\sum_{i=0}^{n-1}(i+1)\tbinom{n-1}{i}\\ &=n(\sum_{i=0}^{n-1}i\tbinom{n-1}i+\sum_{i=0}^{n-1}\tbinom{n-1}{i})\\ &=n[(n-1)2^{n-2}+2^{n-1}]\\ &=n(n+1)2^{n-2} \end{aligned} i=0ni2(in)=i=1nin(i1n1)=ni=0n1(i+1)(in1)=n(i=0n1i(in1)+i=0n1(in1))=n[(n1)2n2+2n1]=n(n+1)2n2

  8. ∑ i = 0 m ( n i ) ( m m − i ) = ( m + n m ) , m ≤ n \sum_{i=0}^m\tbinom{n}{i}\tbinom{m}{m-i}=\tbinom{m+n}{m},m\le n i=0m(in)(mim)=(mm+n),mn
    證明:考慮 ( m + n m ) \binom{m+n}m (mm+n)的組合學意義,它代表的是從 n n n個元素的集合 A A A中和 m m m個元素的集合 B B B中選出 m m m個元素,顯然我們可以按照從集合 A A A中選出的元素個數 i i i來對方案進行劃分,集合 A A A中選擇 i i i個元素的方案數為 ( n i ) \binom{n}i (in),還需要從集合 B B B中選擇 m − i m-i mi個元素,有 ( m m − i ) \binom{m}{m-i} (mim),然後根據乘法原理乘起來即可,然後要對所有的 i , i ∈ [ 0 , m ] i,i\in[0,m] ii[0,m]對應的方案數求和,得到 ∑ i = 0 m ( n i ) ( m m − i ) = ( m + n m ) \sum_{i=0}^m\tbinom{n}{i}\tbinom{m}{m-i}=\tbinom{m+n}{m} i=0m(in)(mim)=(mm+n)

  9. ∑ i = 0 n ( n i ) 2 = ( 2 n n ) \sum_{i=0}^n\tbinom{n}i^2=\tbinom{2n}{n} i=0n(in)2=(n2n)
    證明:根據8我們知道 ( 2 n n ) = ∑ i = 0 n ( n i ) ( n n − i ) = ∑ i = 0 n ( n i ) 2 \tbinom{2n}n=\sum_{i=0}^n\tbinom{n}i\tbinom{n}{n-i}=\sum_{i=0}^n\tbinom{n}{i}^2 (n2n)=i=0n(in)(nin)=i=0n(in)2

  10. ( n k ) = ( n − 1 k − 1 ) + ( n − 1 k ) \tbinom{n}{k}=\tbinom{n-1}{k-1}+\tbinom{n-1}{k} (kn)=(k1n1)+(kn1),當 n < k n<k n<k的時候該式子也成立。
    證明:考慮 ( n k ) \tbinom{n}k (kn)的組合學意義,我們要從 n n n個元素中取 k k k個元素,假設其中一個元素為 a a a,那麼我們可以取的方案有兩種,一種是包括 a a a元素的,一種是不包括 a a a元素的。對於第一種方案我們有 ( n − 1 k − 1 ) \tbinom{n-1}{k-1} (k1n1)種取法,對於第二種方案我們有 ( n − 1 k ) \tbinom{n-1}k (kn1)種取法,故有關係式 ( n k ) = ( n − 1 k − 1 ) + ( n − 1 k ) \tbinom{n}{k}=\tbinom{n-1}{k-1}+\tbinom{n-1}{k} (kn)=(k1n1)+(kn1)成立。
    n < k n<k n<k時有 n − 1 < k − 1 , n − 1 < k n-1<k-1,n-1<k n1<k1,n1<k,故有 ( n k ) = ( n − 1 k − 1 ) = ( n − 1 k ) = 0 \tbinom nk=\binom{n-1}{k-1}=\binom{n-1}k=0 (kn)=(k1n1)=(kn1)=0,故公式仍然適用。

  11. ∑ i = 0 n ( i k ) = ( n + 1 k + 1 ) \sum_{i=0}^n\tbinom{i}{k}=\tbinom{n+1}{k+1} i=0n(ki)=(k+1n+1) ,在該式子中 n n n k k k的大小關係可以任意。
    證明:利用10的關係式遞推即可:
    ( 0 k ) + ( 0 k + 1 ) = ( 1 k + 1 ) \binom{0}k+\binom 0{k+1}=\binom 1{k+1} (k0)+(k+10)=(k+11)
    ( 1 k + 1 ) + ( 1 k ) = ( 2 k + 1 ) \binom 1{k+1}+\binom 1k=\binom 2{k+1} (k+11)+(k1)=(k+12)
    ( 2 k + 1 ) + ( 2 k ) = ( 3 k + 1 ) \binom 2{k+1}+\binom 2k=\binom 3{k+1} (k+12)+(k2)=(k+13)

    ( n k + 1 ) + ( n k ) = ( n + 1 k + 1 ) \binom n{k+1}+\binom nk=\binom {n+1}{k+1} (k+1n)+(kn)=(k+1n+1)
    將左邊項加起來,右邊項也加起來就得到 ( 0 k + 1 ) + ∑ i = 0 n ( i k ) = ( n + 1 k + 1 ) \tbinom 0{k+1}+\sum_{i=0}^n\tbinom{i}k=\tbinom{n+1}{k+1} (k+10)+i=0n(ki)=(k+1n+1),由於 k ≥ 0 k\ge 0 k0,故 ( 0 k + 1 ) = 0 \tbinom{0}{k+1}=0 (k+10)=0,因此有 ∑ i = 0 n ( i k ) = ( n + 1 k + 1 ) \sum_{i=0}^n\tbinom{i}k=\tbinom{n+1}{k+1} i=0n(ki)=(k+1n+1)
    又因為我們遞推過程中用到的性質(即性質10)沒有對 n n n k k k之間的大小關係由限制,故式子在 n n n k k k任意大小關係的情況下都成立。

  12. ( n i ) ( i k ) = ( n k ) ( n − k i − k ) = ( n k ) ( n − k n − i ) \tbinom{n}i\tbinom ik=\tbinom nk\tbinom {n-k}{i-k}=\tbinom{n}{k}\tbinom{n-k}{n-i} (in)(ki)=(kn)(iknk)=(kn)(nink),其中 n , i , k n,i,k n,i,k的大小關係任意。
    證明: ( n i ) ( i k ) = n ! i ! ( n − i ) ! i ! k ! ( i − k ) ! = n ! k ! 1 ( i − k ) ! ( n − i ) ! = n ! k ! ( n − k ) ! ( n − k ) ! ( i − k ) ! [ ( n − k ) − ( i − k ) ] ! = ( n k ) ( n − k i − k ) \tbinom{n}i\tbinom ik=\frac {n!}{i!(n-i)!}\frac {i!}{k!(i-k)!}=\frac{n!}{k!}\frac 1{(i-k)!(n-i)!}=\frac {n!}{k!(n-k)!}\frac {(n-k)!}{(i-k)![(n-k)-(i-k)]!}=\tbinom{n}k\tbinom{n-k}{i-k} (in)(ki)=i!(ni)!n!k!(ik)!i!=k!n!(ik)!(ni)!1=k!(nk)!n!(ik)![(nk)(ik)]!(nk)!=(kn)(iknk)。對於 i > n i>n i>n k > i k>i k>i的情況容易驗證也是正確的。

  13. ∑ i = 0 n ( n − i i ) = F n + 1 \sum_{i=0}^n\tbinom{n-i}i=F_{n+1} i=0n(ini)=Fn+1 F n F_n Fn為斐波拉契數列,事實上這個式子代表的是楊輝三角斜對角線之和等於斐波拉契數列,如下圖所示。
    楊輝三角
    證明:由圖中可以直接得證,我們考慮三個連續的相鄰的對角線上的數字的關係,第一個對角線與第二個對角線上相同行的兩個數字相加均可以得到第三個對角線上的數字,如下圖所示(取任意三個連續對角線,紅框表示一個對角線和第二個對角線上的數字,藍框表示第三個對角線上的數字,紫框中的數相加後得到藍色框的對應的數字):楊輝三角證明
    當然也可以直接由公式推導證明,只是沒有這麼直觀,我們有:
    ∑ i = 0 n ( n − i i ) + ∑ i = 0 n + 1 ( n + 1 − i i ) = ∑ i = 1 n + 1 ( n + 1 − i i − 1 ) + ∑ i = 0 n + 1 ( n + 1 − i i ) = ∑ i = 1 n + 1 ( ( n + 1 − i i − 1 ) + ( n + 1 − i i ) ) + 1 = ∑ i = 1 n + 1 ( n + 2 − i i ) + ( n + 2 0 ) = ∑ i = 0 n + 1 ( n + 2 − i i ) + 0 = ∑ i = 0 n + 1 ( n + 2 − i i ) + ( 0 n + 2 ) = ∑ i = 0 n + 2 ( n + 2 − i i ) \begin{aligned} \sum_{i=0}^n\tbinom{n-i}{i}+\sum_{i=0}^{n+1}\tbinom{n+1-i}i&=\sum_{i=1}^{n+1}\tbinom{n+1-i}{i-1}+\sum_{i=0}^{n+1}\tbinom{n+1-i}{i}\\ &=\sum_{i=1}^{n+1}(\tbinom{n+1-i}{i-1}+\tbinom{n+1-i}{i})+1\\ &=\sum_{i=1}^{n+1}\tbinom{n+2-i}{i}+\tbinom{n+2}{0}\\ &=\sum_{i=0}^{n+1}\tbinom{n+2-i}i+0\\ &=\sum_{i=0}^{n+1}\tbinom{n+2-i}i+\tbinom{0}{n+2}\\ &=\sum_{i=0}^{n+2}\tbinom{n+2-i}i\\ \end{aligned} i=0n(ini)+i=0n+1(in+1i)=i=1n+1(i1n+1i)+i=0n+1(in+1i)=i=1n+1((i1n+1i)+(in+1i))+1=i=1n+1(in+2i)+(0n+2)=i=0n+1(in+2i)+0=i=0n+1(in+2i)+(n+20)=i=0n+2(in+2i)
    顯然滿足斐波拉契定義,並且由圖可直接驗證公式在 n = 0 , 1 , 2 n=0,1,2 n=0,1,2的時候都成立,因此公式成立。

4.組合數取模及求解

組合數取模的話有四種方法,分別適用於不同的情況。

(1).Pascal打表法(批量)

根據二.3.10我們有公式 ( n k ) = ( n − 1 k − 1 ) + ( n − 1 k ) \binom{n}{k}=\binom{n-1}{k-1}+\binom{n-1}{k} (kn)=(k1n1)+(kn1)成立,故我們考慮利用這個公式遞推即可,也就是打表,邊界條件為 ( 0 0 ) = 1 \binom 00=1 (00)=1
時間複雜度: O ( n 2 ) O(n^2) O(n2)預處理, O ( 1 ) O(1) O(1)查詢。
空間複雜度: O ( n 2 ) O(n^2) O(n2)
一般適用範圍: n , k ≤ 5000 n,k\le 5000 n,k5000
這裡給出一份參考程式碼。

const int mod = 1e9+7;
int c[maxn][maxn];
void init(int n){
	c[0][0]=1;
	FOR(i,1,n+1){
		c[i][0]=1;
		FOR(j,1,n+1)c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
	}
}

(2).公式法(單個)

利用組合數的定義式 C n k = n ! k ! ( n − k ) ! C_n^k=\frac{n!}{k!(n-k)!} Cnk=k!(nk)!n!進行求解,不過這裡有三種不同的方式對公式進行求解。

[1].方法一

考慮預處理出 1 1 1~ n n n的所有數的階乘以及階乘的逆元,於是可以直接代入式子計算。關於階乘逆元的求解原理詳見基礎數論學習筆記(上)第五部分1.(3).[4].情況二
時間複雜度: O ( n ) O(n) O(n)預處理, O ( 1 ) O(1) O(1)查詢
空間複雜度: O ( n ) O(n) O(n)
一般適用範圍: n , k ≤ 1 e 8 n,k\le 1e8 n,k1e8
這裡給出一份參考程式碼。

const int mod = 1e9+7;
int fac[maxn],fav[maxn];//fac[n]=n!,fav[n]=inv(fac[n]) 
void init(int n){//預處理階乘和它的逆元
	fac[0]=1;
	FOR(i,1,n+1)fac[i]=1ll*fac[i-1]*i%mod;
	fav[n]=qpow(fac[n],mod-2,mod);
	ROF(i,n-1,0)fav[i]=1ll*(i+1)*fav[i+1]%mod;
}
int C(int n,int k){
	if(n<k)return 0;
	return 1ll*fac[n]*fav[k]%mod*fav[n-k]%mod;
}

[2].方法二

仍然考慮預處理出 1 1 1~ n n n的階乘的逆元,然後由於的 C n k = n ! k ! ( n − k ) ! = n ( n − 1 ) ( n − 2 ) . . . ( n − k + 1 ) k ! C_n^k=\frac {n!}{k!(n-k)!}=\frac{n(n-1)(n-2)...(n-k+1)}{k!} Cnk=k!(nk)!n!=k!n(n1)(n2)...(nk+1),注意到分子和分母其實都只有 k k k項,因此在 n n n很大但是 k k k很小的時候可以暴力處理,其中分母可以預處理它的逆元,分子可以暴力 O ( k ) O(k) O(k)計算。
時間複雜度: O ( n ) O(n) O(n)預處理, O ( k ) O(k) O(k)查詢
空間複雜度: O ( n ) O(n) O(n)
一般適用範圍: n ≤ 1 e 18 n\le 1e18 n1e18, k ≤ 1 e 8 k\le 1e8 k1e8
這裡給出一份參考程式碼。

const int mod = 1e9+7;
int fac[maxn],fav[maxn];//fac[n]=n!,fav[n]=inv(fac[n]) 
void init(int n){
	fac[0]=1;
	FOR(i,1,n+1)fac[i]=1ll*fac[i-1]*i%mod;
	fav[n]=qpow(fac[n],mod-2,mod);
	ROF(i,n-1,0)fav[i]=1ll*(i+1)*fav[i+1]%mod;
}

int C(ll n,int k){
	if(n<k)return 0;
	int ans=1;
	FOR(i,0,k)ans=1ll*ans*(((n-i))%mod)%mod;
	return 1ll*ans*fav[k]%mod;
}

[3].方法三

當模數不為質數的時候前兩個方法都將無法使用,因為我們無法求出階乘的逆元,這時候我們可以考慮計算質數因子對組合數的貢獻。具體地,我們設 c n t [ i ] cnt[i] cnt[i]表示對答案的貢獻,令 m i n p [ i ] minp[i] minp[i]表示 i i i的最小質因子,由於 ( n k ) = ∏ i = n − k + 1 n i ∏ i = 1 k i \tbinom{n}k=\frac{\prod_{i=n-k+1}^ni}{\prod_{i=1}^ki} (kn)=i=1kii=nk+1ni,因此我們初始化 c n t [ n − k + 1 ∼ n ] cnt[n-k+1\sim n] cnt[nk+1n] 1 1 1以及 c n t [ 1 ∼ k ] cnt[1\sim k] cnt[1k] − 1 -1 1,重合部分設成 0 0 0。然後考慮把每個合數 c n t cnt cnt的貢獻分解為質數 c n t cnt cnt的貢獻。轉移方程也很容易寫出來,顯然有 c n t [ m i n p [ i ] ] + = c n t [ i ] , c n t [ i m i n p [ i ] ] + = c n t [ i ] cnt[minp[i]]+=cnt[i],cnt[\frac i{minp[i]}]+=cnt[i] cnt[minp[i]]+=cnt[i],cnt[minp[i]i]+=cnt[i],如果從大到小對每個合數進行轉移的話就能夠把合數的貢獻分解為每個質數的貢獻,最後我們把所有質數的貢獻全部乘起來即可。注意這些貢獻指的是冪次,故需要用到快速冪,轉移複雜度是 O ( n ) O(n) O(n)的,但是由於用到快速冪的緣故會導致複雜度變成 O ( n l o g ( n ) ) O(nlog(n)) O(nlog(n))

時間複雜度: O ( n l o g ( n ) ) O(nlog(n)) O(nlog(n))

空間複雜度: O ( n ) O(n) O(n)

一般適用範圍:模數為合數, n , k ≤ 1 e 6 n,k\le 1e6 n,k1e6,單值查詢

這裡給出一份參考程式碼。

int prim[maxn],tot,minp[maxn],cnt[maxn];
void init(int n){
	FOR(i,2,n+1){
		if(!minp[i])prim[tot++]=i;
		for(register int j =0;j<tot && prim[j]*i<=n;++j){
			minp[i*prim[j]]=prim[j];
			if(i%prim[j]==0)break;
		}
	} 
}
int C(int n,int k,int mod){
	if(k>n-k)k=n-k;
	FOR(i,1,k+1)cnt[i]--;
	ROF(i,n,n-k+1)cnt[i]++;
	ROF(i,n,2){
		if(minp[i]){
			cnt[minp[i]]+=cnt[i];
			cnt[i/minp[i]]+=cnt[i];
		}
	}
	register int ans=1;
	FOR(i,2,n+1)if(cnt[i] && !minp[i])ans=1ll*ans*qpow(i,cnt[i],mod)%mod;
	return ans;
}
int main(){
	init(1000);
	wrn(C(5,3,7));
} 

(3).盧卡斯定理

盧卡斯定理在基礎數論學習筆記(上)第四部分8.(1)有詳細的講解,這裡再給出簡單的介紹。
首先是三個前置引理(下文中的 p p p均是質數):

  1. 組合數是整數。
    證明:根據組合數的意義顯然正確。

  2. p ∣ ( p i ) , i ∈ [ 1 , p − 1 ] p\mid \tbinom{p}{i},i\in[1,p-1] p(ip),i[1,p1]
    證明: ( p i ) = p ( p − 1 ) . . . ( p − i + 1 ) i ! \tbinom pi=\frac{p(p-1)...(p-i+1)}{i!} (ip)=i!p(p1)...(pi+1),由於組合數是整數且 g c d ( i ! , p ) = 1 gcd(i!,p)=1 gcd(i!,p)=1,因此有 i ! ∣ ( p − 1 ) ( p − 2 ) . . . ( p − i + 1 ) i!\mid (p-1)(p-2)...(p-i+1) i!(p1)(p2)...(pi+1),故 p ∣ ( p i ) , i ∈ [ 1 , p − 1 ] p\mid \tbinom{p}{i},i\in[1,p-1] p(ip),i[1,p1]

  3. ( 1 + x ) p ≡ 1 + x p ( m o d p ) (1+x)^{p}\equiv 1+x^p\pmod p (1+x)p1+xp(modp)
    證明:考慮二項式定理和引理2有 ( 1 + x ) p ≡ ( p 0 ) x 0 + ( p 1 ) x 1 + ( p 2 ) x 2 + . . . + ( p p − 1 ) x p − 1 + ( p p ) x p ≡ 1 + x p ( m o d p ) (1+x)^p\equiv \tbinom{p}{0}x^0+\tbinom{p}{1}x^1+\tbinom{p}{2}x^2+...+\tbinom{p}{p-1}x^{p-1}+\tbinom{p}{p}x^{p}\equiv 1+x^p\pmod p (1+x)p(0p)x0+(1p)x1+(2p)x2+...+(p1p)xp1+(pp)xp1+xp(modp)

首先設組合數 ( a b ) , a ≥ b \tbinom{a}{b},a\ge b (ba),ab,我們對 a a a b b b寫成 p p p進位制的形式有 a = a 0 p 0 + a 1 p 1 + . . . + a k p k , b = b 0 p 0 + b 1 p 1 + . . . b k p k a=a_0p^0+a_1p^1+...+a_kp^k,b=b_0p^0+b_1p^1+...b_kp^k a=a0p0+a1p1+...+akpk,b=b0p0+b1p1+...bkpk
於是 ( 1 + x ) a % p (1+x)^a\% p (1+x)a%p可以寫成 ( 1 + x ) a 0 p 0 ( 1 + x ) a 1 p 1 . . . ( 1 + x ) a k p k ≡ ( 1 + x p 0 ) a 0 ( 1 + x p 1 ) a 1 . . . ( 1 + x p k ) a k ( m o d p ) (1+x)^{a_0p^0}(1+x)^{a_1p^1}...(1+x)^{a_kp^k}\equiv (1+x^{p^0})^{a_0}(1+x^{p^1})^{a_1}...(1+x^{p^k})^{a_k}\pmod p (1+x)a0p0(1+x)a1p1...(1+x)akpk(1+xp0)a0(1+xp1)a1...(1+xpk)ak(modp)(這裡套了引理3的變換),然後考慮 ( 1 + x ) a (1+x)^a (1+x)a的第 b b b項的係數顯然為 ( a b ) \tbinom{a}{b} (ba),除此之外根據它還可以表示為從 ( 1 + x p 0 ) a 0 (1+x^{p^0})^{a_0} (1+xp0)a0中選擇 x b 0 p 0 x^{b_0p_0} xb0p0項,從 ( 1 + x p 1 ) a 1 (1+x^{p^1})^{a_1} (1+xp1)a1中選擇 x b 1 p 1 x^{b_1p_1} xb1p1…它們選擇的方案數分別是 ( a 0 b 0 ) , ( a 1 b 1 ) , . . . , ( a k b k ) \tbinom{a_0}{b_0},\tbinom{a_1}{b_1},...,\tbinom{a_k}{b_k} (b0a0),(b1a1),...,(bkak),於是能夠得到 ( a b ) = ( a 0 b 0 ) ( a 1 b 1 ) ⋯ ( a k b k ) \tbinom{a}{b}=\tbinom{a_0}{b_0}\tbinom{a_1}{b_1}\cdots\tbinom{a_k}{b_k} (ba)=(b0a0)(b1a1)(bkak),這也是盧卡斯定理的一種表述形式,不過更常寫成它的遞推形式 ( a b ) = ( ⌊ a p ⌋ ⌊ b p ⌋ ) ( a % p b % p ) \tbinom{a}b=\tbinom{\lfloor \frac ap\rfloor}{\lfloor \frac bp\rfloor}\tbinom{a\%p}{b\%p} (ba)=(pbpa)(b%pa%p)

上述證明可能有個令人困惑的地方,即當 a i < b i a_i<b_i ai<bi的時候如何從 ( 1 + x p i ) a i (1+x^{p^i})^{a_i} (1+xpi)ai中選出 x b i p i x^{b_ip_i} xbipi,確實,沒辦法選出來,根據進位制特性由於我們只能從 ( 1 + x p i ) a i (1+x^{p^i})^{a_i} (1+xpi)ai中組合出 x b i p i x^{b_ip_i} xbipi,無法從其它地方拼湊出 x b i p i x^{b_ip_i} xbipi,故我們可以認為 x b i p i x^{b_ip^i} xbipi這一項不存在,進一步也就是 x b x^b xb不存在。但 ( 1 + x ) a (1+x)^a (1+x)a顯然可以拼湊出 x b x^b xb這一項,為什麼說它不存在呢?這裡不存在的意思是指它的係數為0,係數為0是因為在應用引理3的時候把它的係數給模掉了,故它的係數為0,不過這與組合數的定義是契合的,因為此時有 ( a i b i ) = 0 , i f    b i > a i \tbinom{a_i}{b_i}=0,if\;b_i>a_i (biai)=0,ifbi>ai,從而有 ( a b ) ≡ 0 ( m o d p ) \tbinom{a}{b}\equiv 0\pmod p (ba)0(modp)

然後關於盧卡斯定理有個重要的推論,關於證明這裡不詳述,已經在上面的連結中給出。
其內容主要是: ( n m ) \tbinom{n}{m} (mn)為奇數,則 n & m = m n\&m=m n&m=m

時間複雜度: O ( p ) O(p) O(p)預處理, O ( l o g p n ) O(log_pn) O(logpn)查詢
空間複雜度: O ( p ) O(p) O(p)
適用範圍: n ≤ 1 e 18 , p ≤ 1 e 8 n\le 1e18,p\le 1e8 n1e18,p1e8,其中 p p p必須是質數

這裡以LuoGu的P3807 【模板】盧卡斯定理為例給出一份參考程式碼:

int mod,fac[maxn],fav[maxn];
inline int C(int n,int m){
	if(n<m)return 0;
	return 1ll*fac[n]*fav[m]%mod*fav[n-m]%mod;
}
inline int lucas(int n,int m){
	if(n<m)return 0;
	if(!m)return 1;
	register int cc=C(n%mod,m%mod);
	if(!cc)return 0;
	return 1ll*cc*lucas(n/mod,m/mod)%mod;
}
int main(){
	int t;
	rd(&t);
	while(t--){
		register int n,m,p;
		rd(&n,&m,&p);
		n+=m;
		mod=p;
		fac[0]=1;
		FOR(i,1,p)fac[i]=1ll*fac[i-1]*i%mod;
		fav[p-1]=qpow(fac[p-1],mod-2,mod);
		ROF(i,p-2,0)fav[i]=1ll*fav[i+1]*(i+1)%mod;
		wrn(lucas(n,m));
	}
}

(4).擴充套件盧卡斯

擴充套件盧卡斯在基礎數論學習筆記(上)第四部分8.(2)有詳細的講解,這裡不過多介紹(太懶了不想寫 )
其主要目的是解決 p p p為合數的情況,一般 p p p範圍比較小又是合數,而 n , m n,m n,m範圍很大則需要用到擴充套件盧卡斯。

這裡以LuoGuP4720 【模板】擴充套件盧卡斯為例給出一份參考程式碼。

int a[50],mod[50],cnt; 
inline int getfac(ll n,int p,int pk,int fg){
	if(!n)return 1;
	register int res=1,ans=1;
	FOR(i,1,pk+1)if(i%p)res=1ll*res*i%pk;
	cnt+=fg*(n/p);
	ans=qpow(res,n/pk,pk);
	res=n%pk;
	FOR(i,1,res+1)if(i%p)ans=1ll*ans*i%pk;
	return 1ll*ans*getfac(n/p,p,pk,fg)%pk;
}
void exgcd(int a,int b,int &x,int &y){
	if(!b)x=1,y=0;
	else exgcd(b,a%b,y,x),y-=a/b*x;
}
int inv(int a,int p){
	a%=p;
	int x,y;
	exgcd(a,p,x,y);
	return (x%p+p)%p;
}
inline int C(ll n,ll m,int p,int pk){
	if(n<m)return 0;
	if(!m)return 1;
	cnt=0;
	int a=getfac(n,p,pk,1),b=getfac(m,p,pk,-1),c=getfac(n-m,p,pk,-1);
	return 1ll*a*inv(b,pk)%pk*inv(c,pk)%pk*qpow(p,cnt,pk)%pk;
}
int crt(int *a,int *m,int n){
	int M=1,ans=0;
	FOR(i,0,n)M*=m[i];
	FOR(i,0,n){
		register int Mi=M/m[i],ti=inv(Mi,m[i]);
		ans=(ans+1ll*a[i]*Mi%M*ti%M)%M;
	}
	return ans;
}
int exlucas(ll n,ll m,int p){
	register int tot=0;
	for(register int i=2;i*i<=p;++i){
		if(p%i==0){
			register int pk=1;
			while(p%i==0){
				pk*=i;
				p/=i;
			}
			a[tot++]=C(n,m,i,pk);
			mod[tot-1]=pk;
		}
	}
	if(p>1)a[tot++]=C(n,m,p,p),mod[tot-1]=p;
	return crt(a,mod,tot);
}
int main(){
	ll n,m;int p;
	rd(&n,&m,&p);
	wrn(exlucas(n,m,p));
}

三、特殊排列組合

1.不相鄰組合

從集合 { 1 , 2 , 3 , . . . , n } \{1,2,3,...,n\} {1,2,3,...,n}中選擇 k k k個數,滿足這 k k k個數兩兩均不相鄰的組合方式有 ( n − k + 1 k ) \tbinom{n-k+1}{k} (knk+1)種。

證明:設從 { 1 , 2 , 3 , . . . , n } \{1,2,3,...,n\} {1,2,3,...,n}中選擇 k k k個不相鄰數為 a 1 < a 2 < , . . . , < a k , a i − a i − 1 ≥ 2 , ∀ i ∈ [ 2 , k ] a_1<a_2<,...,<a_k,a_{i}-a_{i-1}\ge 2,\forall i\in[2,k] a1<a2<,...,<ak,aiai12,i[2,k],設從 { 1 , 2 , 3 , . . . , n − k + 1 } \{1,2,3,...,n-k+1\} {1,2,3,...,nk+1}中選擇 k k k個不同的數為 b 1 , b 2 , . . . , b k , b i − b i − 1 ≥ 1 , ∀ i ∈ [ 2 , k ] b_1,b_2,...,b_k,b_i-b_{i-1}\ge 1,\forall i\in[2,k] b1,b2,...,bk,bibi11,i[2,k],我們只需要說明 { a i } \{a_i\} {ai} { b i } \{b_i\} {bi}是一一對映即可。對於任意一個合法的 { a i } \{a_i\} {ai}序列,我們可以根據 { a i } \{a_i\} {ai}構造出這樣的序列 a 1 ′ , a 2 ′ = a 2 − 1 , a 3 ′ = a 3 − 1 , . . . , a k ′ = a k − k + 1 a'_1,a'_2=a_2-1,a'_3=a_3-1,...,a'_k=a_k-k+1 a1,a2=a21,a3=a31,...,ak=akk+1 { a i ′ } \{a'_i\} {ai}序列滿足 a i ′ − a i − 1 ′ = a i − a i − 1 − 1 ≥ 1 , ∀ i ∈ [ 2 , k ] a'_i-a'_{i-1}=a_i-a_{i-1}-1\ge 1,\forall i\in[2,k] aiai1=aiai111,i[2,k] a i ≤ n − k + 1 a_i\le n-k+1 aink+1,故 { a i ′ } \{a'_i\} {ai}對應到一個 { b i } \{b_i\} {bi}序列,顯然每個 { a i } \{a_i\} {ai}都可以通過這種方法轉化為一個 { b i } \{b_i\} {bi}序列,並且這是一個單射。同樣的道理,對於任意一個 { b i } \{b_i\} {bi}序列,我們構造它的對映序列 { b i ′ } \{b'_i\} {bi}滿足 b i ′ = b i + i − 1 , ∀ i ∈ [ 1 , k ] b'_i=b_i+i-1,\forall i\in[1,k] bi=bi+i1,i[1,k],容易驗證 { b i ′ } \{b'_i\} {bi}對應到一個 { a i } \{a_i\} {ai}序列,並且這也是單射,故我們在 { a i } \{a_i\} {ai} { b i } \{b_i\} {bi}之間建立了一個一一對映,這保證了兩種序列的種類數目是一樣多的,而我們知道 { b i } \{b_i\} {bi}的生成方式有 ( n − k + 1 k ) \tbinom{n-k+1}{k} (knk+1)種,故不相鄰排列的組合數目也是 ( n − k + 1 k ) \tbinom{n-k+1}{k} (knk+1)種。

2.錯排列

f ( n ) f(n) f(n)表示 1 1 1~ n n n的數的錯排列的個數,則滿足關係 f ( n ) = ( n − 1 ) ( f ( n − 1 ) + f ( n − 2 ) ) , f ( 1 ) = 0 , f ( 2 ) = 1 f(n)=(n-1)(f(n-1)+f(n-2)),f(1)=0,f(2)=1 f(n)=(n1)(f(n1)+f(n2)),f(1)=0,f(2)=1,並且可以由此推出錯排數通項公式為 f ( n ) = n ! ( 1 2 ! − 1 3 ! + 1 4 ! − . . . + ( − 1 ) n 1 n ! ) = ⌊ n ! e + 0.5 ⌋ f(n)=n!(\frac 1{2!}-\frac 1{3!}+\frac 1{4!}-...+(-1)^n\frac 1{n!})=\lfloor \frac{n!}e+0.5\rfloor f(n)=n!(2!13!1+4!1...+(1)nn!1)=en!+0.5

錯排數的前幾項為: 0 , 1 , 2 , 9 , 44 , 265 0,1,2,9,44,265 0,1,2,9,44,265

遞推式證明:考慮前 n − 1 n-1 n1個數的放置情況來構造錯排列。

  1. 全部錯排:此時第 n n n個數與前面任意一個數交換就變成錯排列了,方案數為 ( n − 1 ) f ( n − 1 ) (n-1)f(n-1) (n1)f(n1)
  2. 有一個沒有錯排,其它全錯排:此時將第 n n n個數與那個沒有錯排的數交換即可變成完整的錯排列,方案數為 ( n − 1 ) f ( n − 2 ) (n-1)f(n-2) (n1)f(n2),並且這種情況與第一種沒有重複。假設有重複方案,我們就考慮這個方案下第 n n n個位置的數,假設它是 a a a,根據第二種方案的構造方式那麼位置 a a a上的數就是 n n n,而根據第一種方案的話位置 a a a上的數一定不是 a a a,矛盾,故假設不成立,即當前方案與第一種方案不存在重複情況。
  3. 其它:此時一定有 k ≥ 2 k\ge 2 k2個數未發生錯排,如果將第 n n n個數與前面任意一個數交換都不可能構成錯排列,如果通過多次交換來構成的錯排列一定也可以由情況1或情況2得到,因此這種情況不會對錯排數種數產生貢獻。

綜上所述我們通過方案一和方案二構造出錯排列,錯排列總數為它們之和,即 f ( n ) = ( n − 1 ) ( f ( n − 1 ) + f ( n − 2 ) ) f(n)=(n-1)(f(n-1)+f(n-2)) f(n)=(n1)(f(n1)+f(n2))

下面給出關於錯排數的通項公式的推導。
首先令 g ( n ) = f ( n ) n ! g(n)=\frac {f(n)}{n!} g(n)=n!f(n),則有 g ( 1 ) = 0 , g ( 2 ) = 1 2 g(1)=0,g(2)=\frac 12 g(1)=0,g(2)=21,根據 f ( n ) = ( n − 1 ) ( f ( n − 1 ) + f ( n − 2 ) ) f(n)=(n-1)(f(n-1)+f(n-2)) f(n)=(n1)(f(n1)+f(n2))我們有:
f ( n ) n ! = ( n − 1 ) ( 1 n ⋅ f ( n − 1 ) ( n − 1 ) + 1 n ( n − 1 ) ⋅ f ( n − 2 ) ( n − 2 ) ! ) ⇒ n g ( n ) = ( n − 1 ) g ( n − 1 ) + g ( n − 2 ) ⇒ n [ g ( n ) − g ( n − 1 ) ] = − [ g ( n − 1 ) − g ( n − 2 ) ] ⇒ g ( n ) − g ( n − 1 ) = − 1 n [ g ( n − 1 ) − g ( n − 2 ) ] ⇒ g ( n ) − g ( n − 1 ) = ( − 1 n ) ( − 1 n − 1 ) ( − 1 n − 2 ) ⋯ ( − 1 3 ) [ g ( 2 ) − g ( 1 ) ] = ( − 1 ) n n ! \begin{aligned}&\frac{f(n)}{n!}=(n-1)(\frac 1n\cdot\frac{f(n-1)}{(n-1)}+\frac 1{n(n-1)}\cdot\frac{f(n-2)}{(n-2)!})\\ &\Rightarrow ng(n)=(n-1)g(n-1)+g(n-2)\\ &\Rightarrow n[g(n)-g(n-1)]=-[g(n-1)-g(n-2)]\\ &\Rightarrow g(n)-g(n-1)=-\frac 1n[g(n-1)-g(n-2)]\\ &\Rightarrow g(n)-g(n-1)=(-\frac 1n)(-\frac 1{n-1})(-\frac 1{n-2})\cdots (-\frac 13)[g(2)-g(1)]=\frac{(-1)^n}{n!} \end{aligned} n!f(n)=(n1)(n1(n1)f(n1)+n(n1)1(n2)!f(n2))ng(n)=(n1)g(n1)+g(n2)n[g(n)g(n1)]=[g(n1)g(n2)]g(n)g(n1)=n1[g(n1)g(n2)]g(n)g(n1)=(n1)(n11)(n21)(31)[g(2)g(1)]=n!(1)n
於是我們可以列出下面的關係式:
g ( n ) − g ( n − 1 ) = ( − 1 ) n n ! g(n)-g(n-1)=\frac {(-1)^{n}}{n!} g(n)g(n1)=n!(1)n
g ( n − 1 ) − g ( n − 2 ) = ( − 1 ) n − 1 ( n − 1 ) ! g(n-1)-g(n-2)=\frac {(-1)^{n-1}}{(n-1)!} g(n1)g(n2)=(n1)!(1)n1
g ( n − 2 ) − g ( n − 3 ) = ( − 1 ) n − 2 ( n − 2 ) ! g(n-2)-g(n-3)=\frac {(-1)^{n-2}}{(n-2)!} g(n2)g(n3)=(n2)!(1)n2
⋯ \cdots
g ( 2 ) − g ( 1 ) = ( − 1 ) 2 2 ! g(2)-g(1)=\frac {(-1)^{2}}{2!} g(2)g(1)=2!(1)2
將式子全部加起來得到如下關係式:
g ( n ) − g ( 1 ) = ( − 1 ) n n ! + ( − 1 ) n − 1 ( n − 1 ) ! + . . . + ( − 1 ) 2 2 ! g(n)-g(1)=\frac {(-1)^{n}}{n!}+\frac {(-1)^{n-1}}{(n-1)!}+...+\frac {(-1)^{2}}{2!} g(n)g(1)=n!(1)n+(n1)!(1)n1+...+2!(1)2
f ( n ) = n ! g ( n ) , g ( 1 ) = 0 f(n)=n!g(n),g(1)=0 f(n)=n!g(n),g(1)=0代入得到: f ( n ) = n ! ∑ i = 2 n ( − 1 ) i i ! f(n)=n!\sum_{i=2}^n\frac{(-1)^i}{i!} f(n)=n!i=2ni!(1)i,這也是它的通項公式。

通過泰勒公式我們可以對這個公式進一步化簡,考慮 e x = ∑ i = 0 n 1 i ! x i + R n ( x ) e^x=\sum_{i=0}^{n}\frac 1{i!}x^i+R_n(x) ex=i=0ni!1xi+Rn(x),其中 R n ( x ) R_n(x) Rn(x)是拉格朗日餘項,滿足 R n ( x ) = e ξ ( n + 1 ) ! x n + 1 , ξ ∈ ( 0 , x ) R_n(x)=\frac{e^ξ}{(n+1)!}x^{n+1},ξ\in(0,x) Rn(x)=(n+1)!eξxn+1,ξ(0,x),注意到 e − 1 = 1 − 1 + ( − 1 ) 2 1 2 ! + ( − 1 ) 3 1 3 ! + . . . + ( − 1 ) n 1 n ! + R n ( − 1 ) e^{-1}=1-1+(-1)^2\frac 1{2!}+(-1)^3\frac 1{3!}+...+(-1)^n\frac 1{n!}+R_n(-1) e1=11+(1)22!1+(1)33!1+...+(1)nn!1+Rn(1),因此我們代入 f ( n ) f(n) f(n)表示式有 f ( n ) = n ! [ e − 1 − R n ( − 1 ) ] = n ! e − n ! R n ( − 1 ) f(n)=n![e^{-1}-R_n(-1)]=\frac{n!}{e}-n!R_n(-1) f(n)=n![e1Rn(1)]=en!n!Rn(1),而 ∣ n ! R n ( − 1 ) ∣ = n ! e ξ ( n + 1 ) ! = e ξ n + 1 , ξ ∈ ( − 1 , 0 ) |n!R_n(-1)|=n!\frac{e^ξ}{(n+1)!}=\frac {e^ξ}{n+1},ξ\in (-1,0) n!Rn(1)=n!(n+1)!eξ=n+1eξ,ξ(1,0),從而有 0 < e − 1 2 ≤ e − 1 n + 1 < e ξ n + 1 < e 0 n + 1 ≤ 0.5 0<\frac {e^{-1}}2\le\frac {e^{-1}}{n+1}<\frac{e^ξ}{n+1}<\frac{e^0}{n+1}\le 0.5 0<2e1n+1e1<n+1eξ<n+1e00.5,即 ∣ n ! R n ( − 1 ) ∣ ∈ ( 0 , 0.5 ) |n!R_n(-1)|\in (0,0.5) n!Rn(1)(0,0.5)
又因為 ( n ! e + 0.5 ) − f ( n ) = 0.5 + n ! R n ( − 1 ) (\frac {n!}e+0.5)-f(n)=0.5+n!R_n(-1) (en!+0.5)f(n)=0.5+n!Rn(1)其中 0 < 0.5 + n ! R n ( − 1 ) < 1 0<0.5+n!R_n(-1)<1 0<0.5+n!Rn(1)<1,故 f ( n ) < f ( n ) + [ 0.5 + n ! R n ( − 1 ) ] < f ( n ) + 1 f(n)<f(n)+[0.5+n!R_n(-1)]<f(n)+1 f(n)<f(n)+[0.5+n!Rn(1)]<f(n)+1,而我們知道 f ( n ) f(n) f(n)是一個正整數,因此 f ( n ) = ⌊ f ( n ) ⌋ = ⌊ f ( n ) + [ 0.5 + n ! R n ( − 1 ) ] ⌋ = ⌊ n ! e + 0.5 ⌋ f(n)=\lfloor f(n)\rfloor=\lfloor f(n)+[0.5+n!R_n(-1)] \rfloor=\lfloor \frac{n!}e+0.5\rfloor f(n)=f(n)=f(n)+[0.5+n!Rn(1)]=en!+0.5

從而我們利用泰勒公式證明了 f ( n ) = ⌊ n ! e + 0.5 ⌋ f(n)=\lfloor \frac{n!}e+0.5\rfloor f(n)=en!+0.5注意這個式子只是一個理論公式,它的精度要求隨著 n n n的增大也飛速地增長,事實上 n n n在十多的時候就已經無法保證結果的正確性了,所以一般我們還是遞推地求解 f ( n ) f(n) f(n)

這裡以HDU的不容易系列之(4)——考新郎
為例給出一份參考程式碼。

ll fac[maxn],f[maxn];
inline ll C(int n,int m){
	return fac[n]/(fac[m]*fac[n-m]);
}
int main(){
	fac[0]=f[2]=1;
	FOR(i,1,maxn)fac[i]=fac[i-1]*i;
	FOR(i,3,maxn)f[i]=(i-1)*(f[i-1]+f[i-2]);
	register int t=rd();
	while(t--){
		register int n=rd(),m=rd();
		wrn(f[m]*C(n,n-m));
	} 
}

四、康託展開

1.基本內容

考慮關於一個排列 a [ 1 ] , a [ 2 ] , . . . , a [ n ] a[1],a[2],...,a[n] a[1],a[2],...,a[n]滿足 ∀ 1 ≤ i < j ≤ n , a [ i ] ≠ a [ j ] \forall 1\le i<j\le n,a[i]\ne a[j] 1i<jn,a[i]=a[j],將這個排列記為 a a a,設 r k [ a ] rk[a] rk[a] a a a這個排列在它的所有元素生成的全排列中的字典序的排名,那麼有 r k [ a ] = 1 + ∑ i = 1 n c n t [ i ] ( n − i ) ! rk[a]=1+\sum_{i=1}^ncnt[i](n-i)! rk[a]=1+i=1ncnt[i](ni)!成立,其中 c n t [ i ] = ∑ j = i n [ a [ j ] < a [ i ] ] cnt[i]=\sum_{j=i}^n[a[j]<a[i]] cnt[i]=j=in[a[j]<a[i]],代表 a [ i ] a[i] a[i] a [ j ] a[j] a[j]中小於 a [ i ] a[i] a[i]的元素數目。

證明:考慮比 a [ 1 ] , a [ 2 ] , . . . , a [ n ] a[1],a[2],...,a[n] a[1],a[2],...,a[n]字典序更小的排列有多少個,記這些排列構成的一個集合為 S S S,設 b ∈ S b\in S bS,那麼 S S S中的元素可以按如下方式分類:

  1. b [ 1 ] < a [ 1 ] b[1]<a[1] b[1]<a[1],此時一定有 r k [ b ] < r k [ a ] rk[b]<rk[a] rk[b]<rk[a],然後考慮如何計算這樣的 b b b的種數,我們發現 b [ 1 ] b[1] b[1]的取值有 c n t [ 1 ] cnt[1] cnt[1]種,然後 b [ 2 ] b[2] b[2] b [ n ] b[n] b[n]對應有 ( n − 1 ) ! (n-1)! (n1)!種不同的排列,根據乘法原理有 c n t [ 1 ] ( n − 1 ) ! cnt[1](n-1)! cnt[1](n1)!種符合條件的 b b b序列。
  2. b [ 1 ] = a [ 1 ] 且 b [ 2 ] < a [ 2 ] b[1]=a[1]且b[2]<a[2] b[1]=a[1]b[2]<a[2],此時一定有 r k [ b ] < r k [ a ] rk[b]<rk[a] rk[b]<rk[a],並且此時的 b b b一定不會與情況一種的 b b b產生交集,類似情況一的計算方式容易發現有 c n t [ 2 ] ( n − 2 ) ! cnt[2](n-2)! cnt[2](n2)!種符合條件的 b b b序列。
  3. b [ 1 ] = a [ 1 ] 且 b [ 2 ] = a [ 2 ] 且 b [ 3 ] < a [ 3 ] b[1]=a[1]且b[2]=a[2]且b[3]<a[3] b[1]=a[1]b[2]=a[2]b[3]<a[3],同理得符合條件的 b b b序列有 c n t [ 3 ] ( n − 3 ) ! cnt[3](n-3)! cnt[3](n3)!種。

我們不難發現 B B B集合可以被上述方式不重不漏的劃分,也就是每個 b b b都會被計算到答案中,每個小於 a a a的排列都會被計算,也因此小於 a a a的排列的個數為 ∑ i = 1 n c n t [ i ] ( n − i ) ! \sum_{i=1}^ncnt[i](n-i)! i=1ncnt[i](ni)!,因為我們求解的是 a a a的排名,因此最後還要加一。

2.康擴充開程式碼實現

具體實現康託展開的時候,如果採用暴力的方式每次去計算 c n t [ i ] cnt[i] cnt[i]都是 O ( n ) O(n) O(n)的複雜度,因此總複雜度是 O ( n 2 ) O(n^2) O(n2),十分不優秀。
這裡考慮利用樹狀陣列優化,我們從 i = n i=n i=n開始遍歷直到 i = 1 i=1 i=1,每次都計算完 c n t [ i ] ( n − i ) ! cnt[i](n-i)! cnt[i](ni)!的貢獻時先查詢樹狀陣列中比 a [ i ] a[i] a[i]小的元素有多少個,就算完貢獻後再把 a [ i ] a[i] a[i]加入到樹狀陣列中。

這裡以LuoGuP5367 【模板】康託展開為例給出一份參考程式碼。

int a[maxn],cnt[maxn];
int main(){
	register int n=rd(),ans=0,fac=1,q;
	ROF(i,n,1)a[i]=rd();
	for(register int i=1;i<=n;++i){
		q=0;
		for(register int x=a[i];x;x-=x&-x)q+=cnt[x];//樹狀陣列查詢 
		add(ans,1ll*q*fac%mod);
		fac=1ll*fac*i%mod;//階乘 
		for(register int x=a[i];x<=n;x+=x&-x)cnt[x]++;//樹狀陣列修改 
	}
	wrn(ans+1);
}

3.逆康託展開

逆康託展開其實就是給定一個排列的排名 r k [ a ] rk[a] rk[a]然後讓把這個排列 a a a給還原出來。
觀察 r k [ a ] = 1 + ∑ i = 1 n c n t [ i ] ( n − i ) ! rk[a]=1+\sum_{i=1}^ncnt[i](n-i)! rk[a]=1+i=1ncnt[i](ni)!這個式子,知道 r k [ a ] rk[a] rk[a]後,我們先讓 r k [ a ] rk[a] rk[a]減去一,於是能得到這樣一個數 c n t [ 1 ] ( n − 1 ) ! + c n t [ 2 ] ( n − 2 ) ! + . . . + c n t [ n ] cnt[1](n-1)!+cnt[2](n-2)!+...+cnt[n] cnt[1](n1)!+cnt[2](n2)!+...+cnt[n],我們只要能求出 c n t cnt cnt的值即可還原出 a a a這個排列,由於 ( n − 1 ) ! > ∑ i = 2 n c n t [ i ] ( n − i ) ! (n-1)!>\sum_{i=2}^ncnt[i](n-i)! (n1)!>i=2ncnt[i](ni)!,故得到 c n t [ 1 ] cnt[1] cnt[1]我們只需要讓 r k [ a ] − 1 rk[a]-1 rk[a]1除以 ( n − 1 ) ! (n-1)! (n1)!即可,那麼 ( r k [ a ] − 1 ) % ( n − 1 ) ! (rk[a]-1)\%(n-1)! (rk[a]1)%(n1)!其實就是 ∑ i = 2 n c n t [ i ] ( n − i ) ! \sum_{i=2}^ncnt[i](n-i)! i=2ncnt[i](ni)!的值,然後重複相似的操作求出 c n t [ 2 ] , c n t [ 3 ] . . . . cnt[2],cnt[3].... cnt[2],cnt[3]....即可。

4.逆康擴充開程式碼實現

這裡以UVAPermutation為例給出一份參考程式碼,其中用到了權值線段樹查詢當前剩餘的數中第 k k k大的數。

int t[maxn<<2];
void build(register int rt,register int l,register int r){
	if(l==r){
		t[rt]=1;
		return;
	}
	register int mid= l+r>>1;
	build(rt<<1,l,mid);
	build(rt<<1|1,mid+1,r);
	t[rt]=t[rt<<1]+t[rt<<1|1]; 
}
int qry(register int rt,register int l,register int r,register int k){
	if(l==r){
		t[rt]=0;
		return l;
	}
	register int mid= l+r>>1,ans=0;
	if(t[rt<<1]>=k)ans=qry(rt<<1,l,mid,k);else ans=qry(rt<<1|1,mid+1,r,k-t[rt<<1]);
	t[rt]=t[rt<<1]+t[rt<<1|1];
	return ans;
}
int main(){
	register int t=rd(),k;
	while(t--){
		k=rd();
		build(1,1,k);
		FOR(i,1,k+1){
			register int x=rd()+1;
			wr(qry(1,1,k,x));
			if(i!=k)putchar(' ');
		}
		puts("");
	} 
}

五、卡特蘭數

1.定義

設一個長度為 2 n 2n 2n的由 − 1 , 1 -1,1 1,1構成的序列 a 1 , a 2 , . . . , a 2 n a_1,a_2,...,a_{2n} a1,a2,...,a2n,其中 1 1 1 − 1 -1 1的數量各為 n n n,並且將滿足 ∑ i = 1 x a i ≥ 0 , ∀ x ∈ [ 1 , 2 n ] \sum_{i=1}^{x}a_i\ge 0,\forall x\in[1,2n] i=1xai0,x[1,2n]的序列的個數被記作卡特蘭數 H n H_n Hn,於是有 H n = ( 2 n n ) − ( 2 n n − 1 ) H_n=\tbinom{2n}{n}-\tbinom{2n}{n-1} Hn=(n2n)(n12n)

證明:設 f ( x ) = ∑ i = 1 x a i f(x)=\sum_{i=1}^xa_i f(x)=i=1xai,我們畫出關於整點函式 y = f ( x ) y=f(x) y=f(x)在座標軸上的折線圖:
卡特蘭數圖一
上圖是一個合法的序列對應的折線圖,滿足 f ( x ) ≥ 0 , ∀ x ∈ [ 1 , 2 n = 16 ] f(x)\ge 0,\forall x\in[1,2n=16] f(x)0,x[1,2n=16]
但我們考慮它的反面,即不合法的序列有多少種,於是隨便畫一個不合法的序列對應的折線圖,如下圖所示:
卡特蘭數圖二
我們發現 f ( 9 ) = − 1 < 0 f(9)=-1<0 f(9)=1<0,顯然是一個不合法的序列。此外對於任何一個不合法的序列而言都一定有 ∃ k ∈ [ 1 , 2 n ] , f ( k ) = − 1 \exist k\in[1,2n],f(k)=-1 k[1,2n],f(k)=1,比如說上圖中 ∃ k = 9 , f ( k = 9 ) = − 1 < 0 \exist k=9,f(k=9)=-1<0 k=9,f(k=9)=1<0。利用這個特點我們可以考慮將折線在 x ≥ k x\ge k xk的部分以 y = − 1 y=-1 y=1為軸進行上下翻轉,於是能夠得到如下圖所示(紫色部分是原來的部分,紅色部分是由紫色部分沿著 y = − 1 y=-1 y=1軸翻轉後得到的部分):
卡特蘭數圖三
我們發現這個折線的終點位於 ( 16 , − 2 ) (16,-2) (16,2)位置,事實上對於任何一個不合法序列進行上圖所示的翻折後都會使得終點位於 ( 2 n , − 2 ) (2n,-2) (2n,2)的位置,並且折線一定會穿過 y = − 1 y=-1 y=1這根線(至少一次)。反過來說,對於任何一個如上圖所示穿過 y = − 1 y=-1 y=1到達 ( 2 n , − 2 ) (2n,-2) (2n,2)點的序列而言,照著剛才翻折的過程再來一遍(即得到實際序列),那麼一定也是一個不合法序列,因為它的折線與 y = − 1 y=-1 y=1存在交點,綜上所有非法序列都對應著如上圖所示的一個折線。

那麼這樣的折線有多少根呢?考慮這個折線的實際意義,它相當於是一個由 n − 1 n-1 n1 1 1 1 n + 1 n+1 n+1 − 1 -1 1構成的長度為 2 n 2n 2n的序列對應的 f f f函式的折線圖,這種序列的構造方式顯然有 ( 2 n n − 1 ) \tbinom{2n}{n-1} (n12n)種,這便是非法序列的數目。

再來看原命題中的所有可能的序列總數目,顯然是 ( 2 n n ) \tbinom{2n}n (n2n)(從 2 n 2n 2n個數中指派 n n n個為 1 1 1),由於合法序列數目=總序列數目-非法序列數目,因此我們有 H n = ( 2 n n ) − ( 2 n n − 1 ) H_n=\tbinom{2n}n-\tbinom{2n}{n-1} Hn=(n2n)(n12n)

2.性質

  1. H n = ( 2 n n ) − ( 2 n n − 1 ) H_n=\tbinom{2n}n-\tbinom{2n}{n-1} Hn=(n2n)(n12n)
    證明:見定義。

  2. 卡特蘭數的前幾項為: 1 , 1 , 2 , 5 , 14 , 42 , 132 , . . . 1,1,2,5,14,42,132,... 1,1,2,5,14,42,132,...(從0開始)

  3. H n = ( 2 n n ) n + 1 H_n=\frac{\tbinom{2n}n}{n+1} Hn=n+1(n2n)
    證明:根據性質1我們有 H n = ( 2 n ) ! n ! n ! − ( 2 n ) ! ( n − 1 ) ! ( n + 1 ) ! = ( 2 n ) ! n ! n ! − n n + 1 ( 2 n ) ! n ! n ! = 1 n + 1 ( 2 n ) ! n ! n ! = ( 2 n n ) n + 1 H_n=\frac{(2n)!}{n!n!}-\frac{(2n)!}{(n-1)!(n+1)!}=\frac{(2n)!}{n!n!}-\frac{n}{n+1}\frac{(2n)!}{n!n!}=\frac 1{n+1}\frac {(2n)!}{n!n!}=\frac{\tbinom{2n}n}{n+1} Hn=n!n!(2n)!(n1)!(n+1)!(2n)!=n!n!(2n)!n+1nn!n!(2n)!=n+11n!n!(2n)!=n+1(n2n)

  4. H n = { 4 n − 2 n + 1 H n − 1 i f    n ≥ 1 1 i f    n = 0 H_n=\begin{cases}\frac{4n-2}{n+1}H_{n-1}&if \;n\ge 1\\1&if\;n=0\end{cases} Hn={n+14n2Hn11ifn1ifn=0
    證明:根據性質1我們有 H n = 1 n + 1 ( 2 n ) ! n ! n ! , H n − 1 = 1 n ( 2 n − 2 ) ! ( n − 1 ) ! ( n − 1 ) ! H_n=\frac 1{n+1}\frac{(2n)!}{n!n!},H_{n-1}=\frac 1n\frac{(2n-2)!}{(n-1)!(n-1)!} Hn=n+11n!n!(2n)!,Hn1=n1(n1)!(n1)!(2n2)!,兩式相除有 H n H n − 1 = 4 n − 2 n + 1 \frac{H_n}{H_{n-1}}=\frac{4n-2}{n+1} Hn1Hn=n+14n2,即得原命題成立。

  5. H n = { ∑ i = 0 n − 1 H i H n − 1 − i i f    n ≥ 2 1 i f    n = 0 , 1 H_n=\begin{cases}\sum_{i=0}^{n-1}H_{i}H_{n-1-i}&if\;n\ge2\\1&if\;n=0,1\end{cases} Hn={i=0n1HiHn1i1ifn2ifn=0,1
    證明:這裡需要用到 母函式(生成函式) 的知識(見第十部分)。

  6. H n = 1 n + 1 ∑ i = 0 n ( n i ) 2 H_n=\frac 1{n+1}\sum_{i=0}^{n}\tbinom{n}i^2 Hn=n+11i=0n(in)2
    證明:根據第二部分3.9易得公式成立。

  7. △ H n ∼ 4 n n 3 2 π △H_n\sim \frac{4^n}{n^{\frac 32}\sqrt \pi} Hnn23π 4n
    證明:證明較複雜,感興趣的可以去網上搜尋相關資料。

在具體的求解題目的時候往往要根據資料範圍來選擇以上合適的公式。

3.常用模型

這些模型本質上都是對於卡特蘭數定義方式的一種對映,理解了定義基本就能理解這些模型的原理,下面給出具體的說明。

  1. 設一個棧的進棧序列為 1 , 2 , 3 , . . . , n 1,2,3,...,n 1,2,3,...,n,那麼它的出棧序列的種數為 H n H_n Hn
    證明:考慮每次操作,進棧代表 + 1 +1 +1,出棧代表 − 1 -1 1,於是進出棧可以表示成一個長為 2 n 2n 2n的操作序列 a 1 , a 2 , . . . , a 2 n , ∀ i ∈ [ 1 , 2 n ] , a i ∈ { − 1 , 1 } a_1,a_2,...,a_{2n},\forall i\in[1,2n],a_i\in\{-1,1\} a1,a2,...,a2n,i[1,2n],ai{1,1},並且這個序列中有 n n n個1與 n n n − 1 -1 1,由於棧中元素個數不可能為負數,故 ∑ i = 1 x a i ≥ 0 , ∀ x ∈ [ 1 , 2 n ] \sum_{i=1}^xa_i\ge 0,\forall x\in[1,2n] i=1xai0,x[1,2n],根據卡特蘭數的定義我們知道這樣的操作序列有 H n H_n Hn種。不過是否每一種操作序列都對應一個獨一無二的出棧序列呢?會不會有兩個操作序列對應同一種出棧序列呢?顯然不會,這是因為每一個出棧序列都可以被唯一地還原為操作序列。
  2. n n n對括號的匹配方式有 H n H_n Hn種。
    證明:將 ( ( (看成是 + 1 +1 +1操作,將 ) ) )看成是 − 1 -1 1操作,顯然滿足卡特蘭數的操作字首和不為負的性質,故操作種數相應地就是卡特蘭數。
  3. 對於一個長度為 n ≥ 1 n\ge 1 n1矩陣鏈乘 A 1 A 2 . . . A n A_1A_2...A_n A1A2...An,如果通過增加括號的方式來改變他們之間相乘的順序,那麼不同的括號方案有 H n − 1 H_{n-1} Hn1種。(注意不同的括號方案必須保證矩陣相乘的順序之間存在差異,比如 ( A 1 A 2 ) 與 ( A 1 ) ( A 2 ) (A_1A_2)與(A_1)(A_2) (A1A2)(A1)(A2)本質上是同樣的乘法順序)
    證明:考慮令 f ( n ) f(n) f(n) n + 1 n+1 n+1個矩陣之間鏈乘加括號的方案數,假設這 n + 1 n+1 n+1個矩陣分別是 A 1 , A 2 , . . . , A n , A n + 1 A_1,A_2,...,A_n,A_{n+1} A1,A2,...,An,An+1,那麼加括號的方案可以是把整體先劃分為兩塊,這兩塊可以是 A 1 , A 2 A 3 . . . A n + 1 A_1,A_2A_3...A_{n+1} A1,A2A3...An+1,可以是 A 1 A 2 , A 3 A 4 . . . A n + 1 A_1A_2,A_3A_4...A_{n+1} A1A2,A3A4...An+1,還可以是 A 1 A 2 A 3 , A 4 A 5 . . . A n + 1 A_1A_2A_3,A_4A_5...A_{n+1} A1A2A3,A4A5...An+1等等以此類推,於是我們總共有 n n n種劃分方式,對於這兩塊而言,我們考慮最後算它們之間的乘積,於是可以這樣加括號 ( 第 一 塊 ) ∗ ( 第 二 塊 ) (第一塊)*(第二塊) ()(),然後在這兩個塊內部再分別考慮如何加括號,顯然這樣劃分的話不會出現重複的方案,因為這裡面每個方案的最後一次乘法都是互不相同的,故方案一定不同,並且由於考慮了所有的劃分情況,也不會出現遺漏。對於每一塊來說,它內部的括號劃分方案數為 f ( s i z e − 1 ) f(size-1) f(size1)(其中 s i z e size size代表塊中元素數量),於是總方案數滿足 f ( n ) = ∑ i = 0 n − 1 f ( i ) f ( n − 1 − i ) f(n)=\sum_{i=0}^{n-1}f(i)f(n-1-i) f(n)=i=0n1f(i)f(n1i),注意到 f ( 0 ) = f ( 1 ) = 1 f(0)=f(1)=1 f(0)=f(1)=1,根據卡特蘭數的性質5我們知道 f ( n ) = H n f(n)=H_n f(n)=Hn,因此我們知道長度為 n n n的矩陣鏈乘的括號分配方案是 H n − 1 H_{n-1} Hn1種。
  4. n n n個節點構成的二叉樹有 H n H_n Hn種。
    證明:首先根節點必定佔用一個節點,然後考慮根節點的左子樹分配 i i i個節點,那麼右子樹必定分配 n − 1 − i n-1-i n1i個節點,其中 i ∈ [ 0 , n − 1 ] i\in[0,n-1] i[0,n1],假設 n n n個節點構成的二叉樹有 f ( n ) f(n) f(n)種,那麼有關係式 f ( n ) = ∑ i = 0 n − 1 f ( i ) f ( n − 1 − i ) f(n)=\sum_{i=0}^{n-1}f(i)f(n-1-i) f(n)=i=0n1f(i)f(n1i)成立,由於 f ( 0 ) = f ( 1 ) = 1 f(0)=f(1)=1 f(0)=f(1)=1,故滿足卡特蘭數的性質5,因此 f ( n ) = H n f(n)=H_n f(n)=Hn
  5. 在圓上取 2 n 2n 2n個不同的點,然後將它們兩兩配對連線後滿足線段不交叉的方案數是 H n H_n Hn
    證明:設 2 n 2n 2n個點對應的方案數為 f ( n ) f(n) f(n),考慮這 2 n 2n 2n個點中某一個確切的點的配對情況,將它與它的配對點連起來得到一根線段,假設這根線段的左邊部分有 2 i 2i 2i個點,右邊部分則有 2 ( n − i − 1 ) 2(n-i-1) 2(ni1)個點,於是方案數為 f ( n ) = ∑ i = 0 n − 1 f ( i ) f ( n − i − 1 ) f(n)=\sum_{i=0}^{n-1}f(i)f(n-i-1) f(n)=i=0n1f(i)f(ni1),根據 f ( 0 ) = f ( 1 ) = 1 f(0)=f(1)=1 f(0)=f(1)=1以及卡特蘭數的性質5我們有 f ( n ) = H n f(n)=H_n f(n)=Hn
  6. 將一個 n ≥ 3 n\ge3 n3個頂點的凸多邊形劃分成若干個三角形區域直到不可再分,這樣的劃分方案為 H n − 2 H_{n-2} Hn2
    證明:考慮凸多邊形上的一條確切的邊 A B AB AB,我們再剩餘的點中選擇一個頂點 C C C,連線 A B C ABC ABC得到了一個三角形,那麼在三角形的左邊是 i ≥ 2 i\ge 2 i2(三角形的側邊上的兩個頂點也算進)個頂點,三角形的右邊是 n − i + 1 n-i+1 ni+1個頂點,再分治的劃分下去即可,於是有 f ( n ) = ∑ i = 2 n − 1 f ( i ) f ( n − i + 1 ) f(n)=\sum_{i=2}^{n-1}f(i)f(n-i+1) f(n)=i=2n1f(i)f(ni+1),令 g ( n ) = f ( n + 2 ) g(n)=f(n+2) g(n)=f(n+2),於是我們有 g ( n ) = ∑ i = 0 n − 1 g ( i ) g ( n − 1 − i ) g(n)=\sum_{i=0}^{n-1}g(i)g(n-1-i) g(n)=i=0n1g(i)g(n1i)成立,令 g ( 0 ) = f ( 2 ) = 1 g(0)=f(2)=1 g(0)=f(2)=1的話我們發現遞推式仍然成立,並且有 g ( 1 ) = f ( 3 ) = 1 g(1)=f(3)=1 g(1)=f(3)=1,故我們有 f ( n ) = g ( n − 2 ) = H n − 2 f(n)=g(n-2)=H_{n-2} f(n)=g(n2)=Hn2
  7. n n n個葉子節點的滿二叉樹的種數為 H n − 1 H_{n-1} Hn1
    證明:方便起見,考慮 n + 1 n+1 n+1個葉子節點的滿二叉樹一定有 2 n 2n 2n條邊,現在為每條邊標記一個權值,向左的邊標記為 + 1 +1 +1,向右的邊標記為 − 1 -1 1,然後我們對二叉樹進行中序遍歷,遍歷過程中經過的邊(回溯經過的邊不計)的權值構成一個長度為 2 n 2n 2n的序列,不難發現這個序列中一定有 n n n 1 1 1 n n n − 1 -1 1,這是因為滿二叉樹中的每個節點一定要麼有兩個兒子,要麼沒有兒子,相當於要麼有兩條權值為 1 1 1 − 1 -1 1的邊,要麼就沒有邊,故 1 1 1 − 1 -1 1權值的邊的數量應該是相等的。
    然後考慮這個序列有什麼性質,由於我們是中序遍歷,也就是說對於兩條路而言,一定是先走左邊再走右邊,這意味著序列的字首和一定是大於等於零的,並且不難發現任何一個擁有這個性質的序列都對應著唯一一棵 n + 1 n+1 n+1個葉子節點的滿二叉樹(因為你可以按照序列給出的邊權構造出來這顆二叉樹,可以畫圖理解一下),故這種二叉樹的數量應該是與這個序列的種數相同的,根據卡特蘭數定義我們知道這個序列的種數是卡特蘭數 H n H_n Hn,也就是說 n + 1 n+1 n+1個葉子結點的滿二叉樹的種數等於 H n H_n Hn
  8. 2 n 2n 2n個人排成一行進入劇場。入場費5元。其中只有 n n n個人有一張5元鈔票,另外 n n n人只有10元鈔票,劇院無其它鈔票,那麼有 H n H_n Hn種方法使得賣票行為得以持續而不至於出現無法找零的情況。
    證明:對於 10 10 10元而言,劇院必須要找 5 5 5元,而這 5 5 5元來自前面付 5 5 5元錢的遊客,因此要保證每一時刻進入劇院的 5 5 5元的遊客不少於 10 10 10元的遊客,根據卡特蘭數定義容易得知排隊的方法有 H n H_n Hn種。
  9. n n n個矩形填充一個高度為 n n n的階梯狀圖形的方法個數為 H n H_n Hn,如下圖所示:
    卡特蘭數圖片4
    證明:考慮最左上角的矩形有 n n n種不同的放置方式,因為這個矩形的右下角必定與某一級階梯的角重合,如果不重合的話,就必然有其它的矩形去填充,而我們知道每個矩形最多能填充一個階梯的角,故此時需要大於 n n n個矩形才能將整個階梯填滿(命題要求是恰好 n n n個矩形填充),因此對於左上角的那個矩形而言,它必須選擇一個階梯的角去填充,故它存在 n n n種不同的放置方式。一旦它選擇了某種放置方式(假設它填充了從上往下數第 i i i個階梯的角),整個階梯就被劃分為兩部分了,上部分和下部分,其中上部分是一個高度為 i − 1 i-1 i1的階梯,下部分是一個高度為 n − i n-i ni的階梯,這樣就劃分為子問題了,如果設高度為 n n n的階梯有 f ( n ) f(n) f(n)種填充方式,則 f ( n ) = ∑ i = 1 n f ( i − 1 ) f ( n − i ) = ∑ i = 0 n − 1 f ( i ) f ( n − 1 − i ) f(n)=\sum_{i=1}^nf(i-1)f(n-i)=\sum_{i=0}^{n-1}f(i)f(n-1-i) f(n)=i=1nf(i1)f(ni)=i=0n1f(i)f(n1i),然後根據 f ( 0 ) = f ( 1 ) = 1 f(0)=f(1)=1 f(0)=f(1)=1容易得知 f ( n ) = H n f(n)=H_n f(n)=Hn

4.卡特蘭數延伸-非降路徑數統計

非降路徑指只能向上或向右走的路徑。
下面的座標格式 ( x , y ) (x,y) (x,y)中第一項是橫座標,第二項是豎座標,約定 x , y ≥ 0 x,y\ge 0 x,y0

  1. ( 0 , 0 ) (0,0) (0,0) ( n , m ) (n,m) (n,m)的非降路徑數為 ( n + m n ) \tbinom{n+m}{n} (nn+m)
    證明:顯然每次的選擇可以構成一個序列,如果將向右記作 0 0 0向上記作 1 1 1那麼這就是一個典型的 01 01 01序列,並且恰好有 n n n 0 0 0 m m m 1 1 1,由於 0 , 1 0,1 0,1順序可以任意安排,故有 ( n + m n ) \tbinom{n+m}n (nn+m)種安排方式。
  2. ( 0 , 0 ) (0,0) (0,0) ( n , n ) (n,n) (n,n)的起始點和終點外不穿過(可以接觸) y = x y=x y=x直線的非降路徑數為 2 H n = 2 n + 1 ( 2 n n ) 2H_n=\frac 2{n+1}\tbinom{2n}n 2Hn=n+12(n2n)
    證明:見卡特蘭數定義。
  3. ( 0 , 0 ) (0,0) (0,0) ( n , m ) , ( m ≤ n ) (n,m),(m\le n) (n,m),(mn)的起始點和終點外不穿過(可以接觸) y = x y=x y=x直線的非降路徑數為 ( n + m m ) − ( n + m m − 1 ) \tbinom{n+m}{m}-\tbinom{n+m}{m-1} (mn+m)(m1n+m)
    證明:類似於卡特蘭數定義中的證明,仍然構造出折線圖,然後翻折,容斥一下即可。
  4. ( 0 , 0 ) (0,0) (0,0) ( n , n ) (n,n) (n,n)的起始點和終點外不接觸 y = x y=x y=x直線的非降路徑數為 2 H n − 1 2H_{n-1} 2Hn1
    證明:由於不能觸碰 y = x y=x y=x直線,我們先算出只經過 y = x y=x y=x下方的非降路徑數,那麼只經過 y = x y=x y=x上方的非降路徑數也是相同的。那麼只經過 y = x y=x y=x下方的非降路徑從 ( 0 , 0 ) (0,0) (0,0)點出發,一定會先向右到達 ( 1 , 0 ) (1,0) (1,0)點,並最後會通過 ( n , n − 1 ) (n,n-1) (n,n1)點到達終點 ( n , n ) (n,n) (n,n),故我們直接統計 ( 1 , 0 ) (1,0) (1,0)點到 ( n , n − 1 ) (n,n-1) (n,n1)點不經過 y = x y=x y=x直線的非降路徑條數即可,不過我們可以將橫座標左移一格,使得問題變成從 ( 0 , 0 ) (0,0) (0,0)點出發到達 ( n − 1 , n − 1 ) (n-1,n-1) (n1,n1)並且不經過直線 y = x + 1 y=x+1 y=x+1的非降路徑條數。注意到不經過 y = x + 1 y=x+1 y=x+1直線其實等價於不穿過 y = x y=x y=x直線,故我們進一步把問題轉化為從 ( 0 , 0 ) (0,0) (0,0)點出發到達 ( n − 1 , n − 1 ) (n-1,n-1) (n1,n1)並且不穿過 y = x y=x y=x直線的非降路徑條數,這個問題與2是幾乎完全相同的,把 2 2 2中的 n n n換成 n − 1 n-1 n1即可。

5.習題

例題一
題目來源:LuoGuP1044 棧

題面:
第五部分5.例題一
題解:求出棧序列的可能排列種數,這是卡特蘭數的常用模型,具體分析見第五部分3.1。
程式碼:

int main(){
	register int n=rd();
	register ll ans=1;
	FOR(i,1,n+1)ans=ans*(4*i-2)/(i+1);
	wrn(ans);
} 

例題二
題目來源:LuoGuP1641 [SCOI2010]生成字串

題面:
第五部分5.例題二
題解:卡特蘭數的擴充套件版本,見第五部分4.3。
程式碼:

int fac[maxn];
int C(int n,int m){
	return 1ll*fac[n]*qpow(fac[m],mod-2,mod)%mod*qpow(fac[n-m],mod-2,mod)%mod;
}
int main(){
	register int n=rd(),m=rd();n+=m;
	fac[0]=1;
	FOR(i,1,n+1)fac[i]=1ll*fac[i-1]*i%mod;
	wrn(sub(C(n,m),C(n,m-1)));
} 

例題三
題目來源:LuoGuP2532 [AHOI2012]樹屋階梯

題面:第五部分5.例題三
題解:詳細分析見第五部分3.9。不過由於本題需要用到高精度,因此這裡給出一個java版本程式碼。
程式碼:

import java.io.*;
import java.math.*;
import java.util.*;
public class Main {
	public static void main(String[] args) {
		Scanner cin = new Scanner(new BufferedInputStream(System.in));
		int n = cin.nextInt();
		BigInteger ans = BigInteger.valueOf(1);
		for(int i = 1;i <= n;++i) {
			ans=ans.multiply(BigInteger.valueOf(4*i-2));
			ans=ans.divide(BigInteger.valueOf(i+1));
		}
		System.out.println(ans);
	}
}

例題四
題目來源:LOJ#10238. 「一本通 6.6 練習 9」網格

題面:
第五部分5.例題四
題解:原理同例題二,不過本題需要高精,由於時限卡得比較緊,這裡給出c++的程式碼。程式碼並不是直接用的高精,由於表示式是一個分數,分子和分母都是一堆比較小的數字的乘積,因此可以考慮統計出每個質數對答案的貢獻,最後再將所有的質數乘起來(這裡將使高精)
程式碼:

const int LEN = 10000;
int prim[maxn],flag[maxn],tot,cnt[maxn],ans[maxn*10],anstot;
void init(register int n){
	FOR(i,2,n+1){
		if(!flag[i])prim[tot++]=i;
		for(register int j =0;j<tot && prim[j]*i<=n;++j){
			flag[i*prim[j]]=1;
			if(i%prim[j]==0)break;
		}
	}
}
inline void work(register int n,register int fg){//處理n的每個質因子對答案的貢獻,fg代表權值(1或-1) 
	for(register int i = 0;prim[i]*prim[i]<=n;++i){
		if(n%prim[i]==0){
			 while(n%prim[i]==0){
			 	n/=prim[i];
			 	cnt[prim[i]]+=fg;
			 }
		}
	}
	if(n>1)cnt[n]+=fg;
}
inline void mul(register int x){
	register int i =0,res=0,cur;
	while(ans[i] || res){
		cur=ans[i]*x+res;
		res=cur/LEN;
		ans[i]=cur%LEN;
		i++;
	}
	anstot=i;
}
void print(){
	wr(ans[--anstot]);
	ROF(i,anstot-1,0)wrd(ans[i],4);
	puts("");
}
int main(){
	register int n=rd(),m=rd();n+=m;
	ans[0]=1,anstot=1;
	init(1200);
	FOR(i,n-m+1,n+1)work(i,1);
	FOR(i,1,m+1)work(i,-1);
	work(n-2*m+1,1);
	work(n-m+1,-1);
	FOR(i,1,n+1)while(cnt[i]--)mul(i);
	print();
} 

例題五
題目來源:HDUGame of Connections

題面:
第五部分5.例題五
題解:具體分析見第五部分3.5。本題同上題一樣需要用到高精度,寫法是相似的,都是統計質數貢獻最後高精乘即可,避免了寫高精除。不過本題用java也能過,這裡再給出一份java程式碼。
c++程式碼:

const int LEN = 10000;
int prim[maxn],flag[maxn],tot,cnt[maxn],ans[maxn],anstot;
void init(int n){
	FOR(i,2,n+1){
		if(!flag[i])prim[tot++]=i;
		for(register int j =0;j<tot && prim[j]*i<=n;++j){
			flag[i*prim[j]]=1;
			if(i%prim[j]==0)break;
		}
	}
}
void work(int x,int fg){
	for(register int i=0;prim[i]*prim[i]<=x;++i){
		if(x%prim[i]==0){
			while(!(x%prim[i])){
				x/=prim[i];
				cnt[prim[i]]+=fg;
			}
		}
	}
	if(x>1)cnt[x]+=fg;
}
void mul(int x){
	register int i =0,res=0,cur=0;
	while(ans[i] || res){
		cur=ans[i]*x+res;
		ans[i]=cur%LEN;
		res=cur/LEN;
		i++;
	}
	anstot=i;
}
void print(){
	wr(ans[--anstot]);
	ROF(i,anstot-1,0)wrd(ans[i],4);
	puts("");
}
int main(){
	register int n;
	init(200);
	while((n=rd())!=-1){
		memset(cnt,0,sizeof(int)*(2*n+1));
		ans[(anstot=1)-1]=1;
		ROF(i,2*n,n+1)work(i,1);
		FOR(i,1,n+2)work(i,-1);
		FOR(i,1,2*n+1)while(cnt[i]--)mul(i);
		print();
		memset(ans,0,sizeof(int)*(anstot+1));
	}
} 

java程式碼:

import java.io.*;
import java.util.*;
import java.math.*;
public class Main {
	public static void main(String[] args) {
		Scanner cin = new Scanner(new BufferedInputStream(System.in));
		int n = cin.nextInt();
		while(n!=-1) {
			BigInteger ans = BigInteger.valueOf(1);
			for(int i = 1;i <= n;++i) {
				ans=ans.multiply(BigInteger.valueOf(4*i-2));
				ans=ans.divide(BigInteger.valueOf(i+1));
			}
			System.out.println(ans);
			n=cin.nextInt();
		}
	}
}

例題六
題目來源:LuoGuP3200 [HNOI2009]有趣的數列
題面:
第五部分5.例題六
題解:本題比較有意思,如果不打表不太容易看出答案是卡特蘭數,不過為什麼呢?我們如果手算幾個樣例容易發現一個規律,那就是一旦偶數項被確定那麼奇數項一定被確定,反之若奇數項確定偶數項也確定。於是我們考慮偶數項的約束條件是什麼,不妨設奇數項為 1 1 1,偶數項為 − 1 -1 1,我們把 − 1 , 1 -1,1 1,1都標記到 1 ∼ 2 n 1\sim 2n 12n的位置上,代表該位置被偶數項或奇數項所佔據。於是可以得到 a 1 , a 2 , . . . , a 2 n a_1,a_2,...,a_{2n} a1,a2,...,a2n這樣一個由 − 1 , 1 -1,1 1,1構成的陣列,若 a i = − 1 a_i=-1 ai=1 i i i被劃分到偶數項,若 a i = 1 a_i=1 ai=1 i i i被劃分到奇數項,那麼這個序列可以唯一確定一個題目所要求的序列。原因也很簡單,首先一定有 a 1 = 1 a_1=1 a1=1,如果是 − 1 -1 1的話那麼不符合題目要求,假設最小的使得 a i = − 1 a_i=-1 ai=1 i i i k k k,即 a k = − 1 a_k=-1 ak=1,那麼 1 1 1一定是與 k k k匹配的,假設 1 1 1與後面的某個滿足 a i = − 1 a_i=-1 ai=1 k ′ ( k ′ > k ) k'(k'>k) k(k>k)匹配,那麼 k k k就找不到匹配的奇項了,因為偶數項必須是遞增的順序。因此我們把 1 1 1 k k k匹配了,然後再找到下一個使得 a i a_i ai 1 1 1的項去匹配即可,注意這一項之前的所有 a i = − 1 a_i=-1 ai=1的奇數項都必須已經匹配完畢,否則後面將無法被匹配。總結來說我們只需要求 a i a_i ai的字首和大於等於零即可,這樣每個 a i a_i ai序列都可以被轉化為題目所要求的序列,同樣的道理題目所要求的序列也能被唯一地轉化為 a i a_i ai型別的序列,故兩者數目是相同的,而 a i a_i ai這個序列是一個典型的卡特蘭數的序列,所以答案也就是 H n H_n Hn

具體寫程式碼的時候還是單獨算質因子貢獻,因為本題中的模數不一定要求是質數,故沒法直接使用逆元。因此我們可以對每個質數考慮它對錶達式 H n = ( 2 n n ) n + 1 = ∏ i = n + 2 2 n i ∏ i = 1 n i H_n=\frac{\tbinom{2n}n}{n+1}=\frac{\prod_{i=n+2}^{2n}i}{\prod_{i=1}^ni} Hn=n+1(n2n)=i=1nii=n+22ni的貢獻。不過由於我們需要計算質數對它的倍數產生的冪次的貢獻,故求貢獻的總複雜度實際要大於 O ( n l o g ( l o g ( n ) ) ) O(nlog(log(n))) O(nlog(log(n))),此外計算質數貢獻的時候的複雜度約為 O ( n l o g ( n ) ) O(nlog(n)) O(nlog(n)),先給出這個方法的程式碼:
程式碼:

int prim[maxn],flag[maxn],tot,cnt[maxn];
void init(int n,int a,int b,int c,int d){
	FOR(i,2,n+1){
		if(!flag[i]){
			prim[tot++]=i;
			for(register int j=i,ct=0,x=j;j<=n;j+=i,x=j,ct=0){
				while(x%i==0)ct++,x/=i;//主要是這個地方增加了複雜度使得複雜度不是O(nlog(log(n))) 
				(j>=a && j<=b) && (cnt[i]+=ct);
				(j>=c && j<=d) && (cnt[i]-=ct);
			} 
		} 
		for(register int j = 0;j<tot && prim[j]*i<=n;++j){
			flag[i*prim[j]]=1;
			if(i%prim[j]==0)break;
		}
	} 
}
int main(){
	register int n=rd(),ans=1;mod=rd();
	init(2*n,n+1,2*n,1,n+1);
	FOR(i,1,2*n+1){
		if(cnt[i])ans=1ll*ans*qpow(i,cnt[i],mod)%mod;
	}
	wrn(ans);
}

對於時間複雜度更優秀的做法可以參考第二部分4.(2).[3]。
這裡給出一份參考程式碼。

int prim[maxn],flag[maxn],tot,cnt[maxn];
void init(int n,int x){
	FOR(i,2,n+1){
		if(i<x)cnt[i]=-1;else if(i>x)cnt[i]=1;
		if(!flag[i])prim[tot++]=i;
		for(register int j = 0;j<tot && prim[j]*i<=n;++j){
			flag[i*prim[j]]=prim[j];
			if(i%prim[j]==0)break;
		}
	} 
}
int main(){
	register int n=rd(),ans=1;mod=rd();
	init(2*n,n+1);
	ROF(i,2*n,2){
		if(flag[i]){
			cnt[flag[i]]+=cnt[i];
			cnt[i/flag[i]]+=cnt[i];
		}
	}
	FOR(i,2,2*n+1)if(cnt[i]&&!flag[i])ans=1ll*ans*qpow(i,cnt[i],mod)%mod;
	wrn(ans);
}

六、容斥原理

容斥原理簡單來說就是一個公式

七、貝爾數

八、伯努利數

九、斯特林數

十、母函式

十一、遞推

十二、polya計數

相關文章