1. 程式人生 > >【P2822】組合數問題【NOIP2016】

【P2822】組合數問題【NOIP2016】

題目描述

組合數 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]

具體怎麼求的畫圖看看就知道了,人懶不想打了QAQ

直接上程式碼!

//組合數問題
#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;
}

(嗯,沒屁放了)