组合数表示的是从 n 个物品中选出 m 个物品的方案数。举个例子,从 (1, 2, 3) 三个物品中选择两个物品可以有 (1, 2),(1, 3),(2, 3) 这三种选择方法。
根据组合数的定义,我们可以给出计算组合数的一般公式:
小葱想知道如果给定 n,m 和 k,对于所有的 0 ≤ i ≤ n, 0 ≤ j ≤ min(i,m) 有多少对 (i, j) 满足是 k 的倍数。
输入描述:
第一行有两个整数 t,k,其中 t 代表该测试点总共有多少组测试数据,k 的意义见 「题目描述」。 接下来 t 行每行两个整数 n,m,其中 n,m 的意义见「题目描述」。
输出描述:
t 行,每行一个整数代表对于所有的 0 ≤ i ≤ n, 0 ≤ j ≤ min(i,m) 有多少对 (i, j) 满足是 k 的倍数。
示例1
输入
1 2 3 3
输出
1
示例2
输入
2 5 4 5 6 7
输出
0 7
说明
在所有可能的情况中,只有是 2 的倍数。
备注:
3 ≤ n, m ≤ 2000, 2 ≤ k ≤ 21, 1 ≤ t ≤ 10000
思路:这里的数据范围不大,所以可以用递归直接求前2000个组合数(注意这里要先做mod运算,不然会超范围),然后用二维前缀和把不同的 组合对应的结果先存下来
#include<iostream>
using namespace std;
int c[2010][2010],pre[2010][2010];
int t,k;
int n,m;
void init(){
for(int i=0;i<2005;i++){//求组合数
for(int j=0;j<=i;j++){
if(!j)
c[i][j]=1%k;
else
c[i][j]=(c[i-1][j]+c[i-1][j-1])%k;//模运算可以拆开
//if(c[i][j]%k==0)
if(!c[i][j])
pre[i][j]=1;
}
}
for(int i=0;i<2005;i++){//求二维前缀和
for(int j=0;j<2005;j++){
if(i)
pre[i][j]+=pre[i-1][j];
if(j)
pre[i][j]+=pre[i][j-1];
if(i&&j)
pre[i][j]-=pre[i-1][j-1];
}
}
}
int main(){
cin>>t>>k;
init();
while(t--){
cin>>n>>m;
cout<<pre[n][m]<<endl;
}
return 0;
}