一、兩個原理
加法原理
若完成一件事的的方法有類,其中第類方法的包括種不同的方法,且這些方法互不重合,則完成這件事共有:種不同的方法。
乘法原理(分佈計數原理)
若完成一件事需要個步驟,其中第個步驟有種不同的完成方法,且這些步驟互不干擾,則完成這件事共有:中不同的方法。
兩個原理的區別:一個與分類有關,一個與分佈有關;加法原理是"分類完成",乘法原理是"分步完成"。
二、排列及其公式
1.線排列
-
Ⅰ.定義:一般地,從個不同的元素中,取出個元素按照一定的順序拍成一列,叫做從個不同的元素中取出個元素的一個線排列。從個不同元素中取出個元素的所有線排列的個數,叫做從個不同元素中取出個元素的排列數,用符號或者表示。
-
Ⅱ.排列數公式:
-
全排列:從個不同的元素中取出個元素的一個線排列,叫做個元素的全排列數,用來表示。此時,
2.相異元素可重排列
從個不同元素中可以重複地選出個元素的排列,叫做相異元素的可重複排列。其排列總數爲。
3.不全相異元素的排列
如果在個元素中,有個元素彼此相同,有和元素彼此相同…有彼此相同,並且,則這個元素的全排列叫做不全相異元素的全排列。
其排列數公式爲:。
【引例】把3個相同的黃球,2個相同的藍球,4個相同的白球排成一排,問:有多少種不同的排法?
解:符合不全相異元素的排列,使用不全相異元素的排列公式:種。
4.圓排列
從個不同元素中選取出個元素,部分首尾地排成一個圓圈的排列叫做圓排列,其排列方案數爲:
三、組合及其公式
1.非重組合
- Ⅰ.定義: 一般地,從個不同的元素中,取出個元素,不允許元素重複,不考慮次序,叫做從個不同的元素中取出個元素的一個非重組合;從個不同元素中取出個元素的所有組合的個數,叫做從個不同個元素中取出個元素的組合數,用符號表示。根據乘法原理(分佈計數原理)可知:。
- Ⅱ.組合數公式:
- Ⅲ.組合數的三個性質:
1、,規定:
2、
3、
2.可重複組合
從個不同元素中,取出個元素組成一個組合,且允許這個元素重複使用(一般,但也允許),則稱這樣的組合爲可重複組合,器組合數記爲個
3.二項式定理
【例題1】luogu P1313 計算係數 (NOIP 2011)
題意
給定一個多項式,請求出多項式展開後項的係數。
思路
直接二項式定理分解。
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long
using namespace std;
const int Max = 1e4 + 10;
const int mod = 10007;
ll qpow(ll a,ll b){
int res = 1;
while(b){
if(b & 1){
res = res * a % mod;
}
a = a * a % mod;
b >>= 1;
}
return res;
}
int c[Max] = {0};
int main(){
ll a,b,k,n,m;
ll ans = 1;
scanf("%lld %lld %lld %lld %lld",&a,&b,&k,&n,&m);
c[1] = k % mod;
for(int i = 2; i <= k; i++){
c[k - i] = c[i] = (k - i + 1) % mod * c[i-1] % mod * qpow(i,mod-2) % mod;
}
ans = c[m] % mod * qpow(a,n) % mod * qpow(b,m) % mod;
printf("%lld\n",ans);
}
【例題】luogu P1066 進制數
題意
設是個 進制數,並滿足以下條件:
(1)至少是個位的 進制數。
(2)作爲 進制數,除最後一位外,的每一位嚴格小於它右邊相鄰的那一位。
(3)將轉換爲進制數後,則的總位數不超過。
在這裏,正整數和是事先給定的。
問:滿足上述條件的不同的 共有多少個?
思路
四、多重集
1.多重集的排列數
多重集是指包括重複元素的廣義集合。設是由個,個,個,…,個組成的多重集。
的全排列個數爲:
2.多重集的組合數
設是由個,個,個,…,個組成的多重集。設整數從中取出個元素組成一個多重集(不考慮元素的順序),產生的不同多重集的數量爲:
證明:
原問題等價於統計下列集合的數量:,其中並且。因爲,必定有,所以只需要考慮這一條件。
故原問題等價於個0,個構成的全排列數——個把個分成組,每組的的數量對應。而多重集的全排列數爲:
。
五、Lucas定理
作用: 利用模運算快速求出二項式係數C(n,m),適用於不用模運算就無法求出其結果的、具有非常大的n和m的二項式係數。
若是質數,則對於任意整數,有:
也就是把和表示成進制數,對進制下的每一位分別計算組合數,最後再乘起來。
【例題3】一本通OJ 1650:組合
題意
給出組合數 C(n,m) 表示從 n 個元素中選出 m 個元素的方案數。例如 C(5,2)=10,C(4,2)=6。可是當 n,m 比較大的時候,C(n,m) 很大。於是 xiaobo 希望你輸出 C(n,m) mod p 的值。
思路
求組合數,但因爲組合數過大,並且需要取模,所以考慮Lucas定理。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define ll long long
using namespace std;
ll n,m,p,cas;
ll qpow(ll a,ll b,ll mod){
ll res = 1;
while(b){
if(1 & b){
res = res * a % mod;
}
a = a * a % mod;
b >>= 1;
}
return res;
}
ll C(ll n,ll m,ll mod){
if(m > n)
return 0;
if(m == 1)
return n;
if(n == m || m == 0)
return 1;
// ll res1 = 1,res2 = 1,res3 = 1;
// for(int i = 1; i <= n; i++){
// res1 = res1 * i % mod;
// if(i <= m)
// res2 = res2 * i % mod;
// if(i <= n - m)
// res3 = res3 * i % mod;
// }
// return res1 * qpow(res2 * res3 % mod , mod - 2,mod) % mod;
ll res1 = 1,res2 = 1,res3 = 1;
for(int i = n - m + 1; i <= n; i++)
res1 = res1 * i % mod;
for(int i = 1; i <= m; i++)
res2 = res2 * i % mod;
return res1 * qpow(res2 , mod - 2,mod) % mod;
}
ll Lucas(ll n,ll m, ll mod){
if(!m)
return 1;
return C(n % mod,m % mod,mod) * Lucas(n / mod , m / mod, mod) % mod;
}
int main(){
scanf("%lld",&cas);
while(cas--){
scanf("%lld %lld %lld",&n,&m,&p);
printf("%lld\n",Lucas(n,m,p));
}
return 0;
}
六、Catalan數列
給定 個 和個1,它們按照某種順序排成長度爲的序列,滿足任意前綴中的個數都不少於的個數的序列的數量爲。
證明: 令個和個任意排成一個長度爲的序列,若不滿足任意前綴中的個數都不少於的個數,則存在一個最小的位置,使得中有個,個。而把中的所有數位取反後,包含個和個.於是我們得到了有 個 和 個 排成的序列。
同理,令個和個隨意排成一個長度爲的序列,也必定存在一個最小的位置,使得中有個,個。把後面剩下的一半取反,就得到了由個和個排成的、存在一個前綴比多的序列。
因此,以下兩種序列構成一個雙射:
- Ⅰ.由個和個排成的、存在一個前綴比多的序列。
- Ⅱ.由個和個排成的序列。
根據組合數的定義,後者顯然有個。
綜上所述,由個和個排成的、任意前綴中都不少於的序列數量爲:
與Catalan數相關的問題:
- Ⅰ.個左括號和個右括號組成的合法括號的數量爲。
- Ⅱ.經過一個棧,形成的合法出棧序列的數量爲。
- Ⅲ.個節點構成的不同二叉樹的數量爲。
- Ⅳ.在平面直角座標系中,每一步只能向上或者向右走,從走到並且除兩個端點外不結束直線的線路數量爲。