什么是組合數?
- 組合數公式是指從n個不同元素中,任取m(m≤n)個元素並成一組,叫做從n個不同元素中取出m個元素的一個組合;
- 從n個不同元素中取出m(m≤n)個元素的所有組合的個數,叫做n個不同元素中取出m個元素的組合數。用符號c(n,m) 表示
前置知識:排列公式
- 排列公式是建立一個模型,從n個不相同元素中取出m個排成一列(有序)
- 第一個位置可以有n個選擇,第二個位置可以有n-1個選擇(已經有1個放在前一個位置),則同理可知第三個位置可以有n-2個選擇,以此類推第m個位置可以有n-m+1個選擇

組合數公式

- 組合公式的推導是由排列公式去掉重復的部分而來的
- 組合公式對應另一個模型,取出m個成為一組(無序)
- 排列公式建立有序,而組合公式建立為無序,有序情況當然比無序多
通過遞推來求組合數,時間復雜度為O(N^2)
c(n,m)=c(n-1,m-1)+c(n-1,m)
- 從n中選m個元素可以選取某元素的包含與不包含,分成兩類情況,即m個被選擇元素包含了這個元素和m個被選擇元素不包含該元素。
- 前者相當於從n-1個元素中選出m-1個元素的組合,即c(n-1,m-1)
- 后者相當於從n-1個元素中選出m個元素的組合,即c(n-1,m)
代碼如下:
- 預處理所有的情況,a,b范圍在[1,2000]
#include<iostream>
using namespace std;
const int N = 2e3 + 10,mod = 1e9 + 7;
int n;
int a,b;
int c[N][N];
void init(){
for(int i = 0; i < N; i ++ ){
for(int j = 0; j <= i; j ++ ){
if(!j) c[i][j] = 1;
else c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;
}
}
}
int main(){
init();
cin >> n;
while(n -- ){
cin >> a >> b;
cout<<c[a][b]<<endl;
}
return 0;
}
通過組合數公式求組合數,時間復雜度為O(nlogn)

- 用fact[]數組來存放(n!)mod(1e9 + 7)
- 用infact[]數組來存放(n!)的逆元mod(1e9 + 7)
- 求逆元用快速冪和費馬小定理
- 組合數為:fact[a] * infact[b - a] * infact[b]
- 防止超出范圍:fact[a] * infact[b] % mod * infact[a - b] % mod
代碼如下:
- 預處理,a,b范圍在[1,1e5]
#include<iostream>
using namespace std;
#define ll long long
const int N = 1e5 + 10,mod = 1e9 + 7;
int n;
ll fact[N],infact[N];
ll qmi(ll a,ll b,ll p){
ll res = 1%p;
while(b){
if(b & 1) res = res * a % p;
a = a * a % p;
b >>= 1;
}
return res;
}
void init(){
fact[0] = infact[0] = 1;
for(int i = 1; i < N; i ++ ){
fact[i] = fact[i - 1] * i % mod;
infact[i] = infact[i - 1] * qmi(i,mod - 2,mod) % mod;
}
}
int main(){
cin >> n;
while(n -- ){
int a,b;
cin >> a >> b;
printf("%lld\n",fact[a] * infact[b] % mod * infact[a - b] % mod);
}
return 0;
}
