【P2822】組合數問題【NOIP2016】
阿新 • • 發佈:2018-12-19
題目描述
組合數 Cnm 表示的是從 n 個物品中選出 m 個物品的方案數。舉個例子,從 (1,2,3) 三個物品中選擇兩個物品可以有 (1,2),(1,3),(2,3) 這三種選擇方法。根據組合數的定義,我們可以給出計算組合數 Cnm的一般公式: C nm= n!(n−m)!/n! 其中n!=1×2×⋯×n;特別地,定義 0!=1。
小蔥想知道如果給定 n,m 和 k,對於所有的0≤i≤n,0≤j≤min(i,m) 有多少對(i,j) 滿足Cij是 k 的倍數。
輸入輸出格式
輸入格式:
第一行有兩個整數 t,k,其中 t 代表該測試點總共有多少組測試資料,k 的意義見問題描述。
接下來 t 行每行兩個整數 n,m,其中n,m 的意義見問題描述。
輸出格式:
共 t 行,每行一個整數代表所有的0≤i≤n,0≤j≤min(i,m) 中有多少對(i,j) 滿足Cij是 k 的倍數。
輸入輸出樣例
輸入樣例#1:
1 2 3 3
輸出樣例#1:
1
輸入樣例#2:
2 5 4 5 6 7
輸出樣例#2:
0 7
說明
【樣例1說明】
在所有可能的情況中,只有C21 =2是2的倍數。
【資料範圍】
【思路】
由於對於每次輸入,雖然有很多個問題,但k是共用的,所以我們可以事先算出所有的組合數再對k取模,當模為0時即為k的倍數。 組合數可以根據公式遞推求得: C(n,m)=C(n-1,m)+C(n-1,m-1)
因為求的是一共有多少個,所以很容易就能想到字首和。 這裡用的是二維字首和(因為i,j嘛) 二維字首和公式: Sum[i][j]=Sum[i-1][j]+Sum[i][j-1]-Sum[i-1][j-1]+a[i][j]
直接上程式碼!
//組合數問題 #include<bits/stdc++.h> using namespace std; const int MAXN=2001; //C(n,m)=C(n-1,m)+C(n-1,m-1) int C[MAXN][MAXN];//儲存組合數答案(取模後的) int Sum[MAXN][MAXN];//儲存有多少對(字首和) int n,m,k,t; void Calc(){ C[1][0]=C[1][1]=1; for(int i=2;i<MAXN;i++){ C[i][0]=C[i][i]=1; for(int j=1;j<i;j++){ C[i][j]=C[i-1][j]+C[i-1][j-1]; C[i][j]%=k; } } for(int i=2;i<MAXN;i++){ for(int j=1;j<MAXN;j++){ Sum[i][j]=Sum[i-1][j]+Sum[i][j-1]-Sum[i-1][j-1]; if(j<=i&&C[i][j]==0)Sum[i][j]++; } } } int main(){ // freopen("problem.in","r",stdin); // freopen("problem.out","w",stdout); scanf("%d %d",&t,&k); Calc(); while(t--){ scanf("%d %d",&n,&m); printf("%d\n",Sum[n][m]); } return 0; }
(嗯,沒屁放了)