本文版權歸ljh2000和博客園共有,歡迎轉載,但須保留此聲明,並給出原文鏈接,謝謝合作。
本文作者:ljh2000
作者博客:http://www.cnblogs.com/ljh2000-jump/
轉載請注明出處,侵權必究,保留最終解釋權!
題目描述
組合數 CmnCnm 表示的是從 nn 個物品中選出 mm 個物品的方案數。舉個例子,從 (1,2,3)(1,2,3) 三個物品中選擇兩個物品可以有 (1,2),(1,3),(2,3)(1,2),(1,3),(2,3) 這三種選擇方法。根據組合數的定義,我們可以給出計算組合數 CmnCnm 的一般公式:
其中 n!=1×2×⋯×nn!=1×2×⋯×n;特別地,定義 0!=10!=1。
小蔥想知道如果給定 n,mn,m 和 kk,對於所有的 0≤i≤n,0≤j≤min(i,m)0≤i≤n,0≤j≤min(i,m) 有多少對 (i,j)(i,j) 滿足 CjiCij 是 kk 的倍數。
輸入格式
從標准輸入讀入數據。
第一行有兩個整數 t,kt,k,其中 tt 代表該測試點總共有多少組測試數據,kk 的意義見問題描述。
接下來 tt 行每行兩個整數 n,mn,m,其中 n,mn,m 的意義見問題描述。
輸出格式
輸出到標准輸出。
tt 行,每行一個整數代表所有的 0≤i≤n,0≤j≤min(i,m)0≤i≤n,0≤j≤min(i,m) 中有多少對 (i,j)(i,j) 滿足 CjiCij 是 kk 的倍數。
樣例一
input
1 2
3 3
output
1
explanation
在所有可能的情況中,只有 C12=2C21=2 是 22的倍數。
樣例二
input
2 5
4 5
6 7
output
0 7
正解:矩陣前綴和+組合數學
解題報告:
這是一道很簡單的數學題,可以發現其實如果根據組合中的一個基本公式:C(n,m)=C(n-1,m)+C(n-1,m-1),就可以直接遞推出2000以內的所有的組合數。而我們只需要判斷有多少個點對滿足是k的倍數,很容易想到只要對k取模,對於為0的C(i,j)是肯定滿足是k的倍數的。
因為k是所有詢問共用的,可以一開始就預處理出矩陣前綴和,之后每次O(1)查詢就可以了。
注意事項:
很多人在考場上寫的是質因數分解,但是很明顯有一些k並不是質數,所以並不能直接分解,應該先對k進行質因數分解,在對於這些質因數在遞推中分析。
1 #include <iostream> 2 #include <cstdio> 3 #include <cmath> 4 #include <cstring> 5 #include <algorithm> 6 #include <string> 7 #include <ctime> 8 #include <queue> 9 #include <vector> 10 #include <cstdlib> 11 using namespace std; 12 typedef long long LL; 13 const int MAXN = 2011; 14 int T,k,n,m,ans; 15 int C[MAXN][MAXN],a[MAXN][MAXN]; 16 int sum[MAXN][MAXN]; 17 18 void work(){ 19 scanf("%d%d",&T,&k); 20 C[1][0]=C[1][1]=1; 21 for(int i=2;i<=2000;i++){ 22 C[i][0]=1; 23 for(int j=1;j<=i;j++) { 24 C[i][j]=C[i-1][j-1]+C[i-1][j]; 25 C[i][j]%=k; 26 if(C[i][j]==0) { 27 a[i][j]=1; 28 } 29 } 30 } 31 for(int i=1;i<=2000;i++) 32 for(int j=1;j<=2000;j++) 33 sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j]; 34 35 while(T--) { 36 scanf("%d%d",&n,&m); m=min(m,n); 37 printf("%d\n",sum[n][m]); 38 } 39 } 40 41 int main() 42 { 43 work(); 44 return 0; 45 }