【问题描述】
组合数Cmn表示的是从n个物品中选出m个物品的方案数。举个例子,从(1,2,3)三个物品中选择两个物品可以有(1,2),(1,3),(2,3)这三种选择方法。根据组合数的定 义,我们可以给出计算组合数的一般公式:
Cnm=(n!m!)/(n−m)!
其中n! = 1 × 2 × · · · × n
小葱想知道如果给定n,m和k,对于所有的0 <= i <= n,0 <= j <= min(i,m)有多少对(i,j)满足Cji是k的倍数。
【输入格式】
第一行有两个整数t,k,其中t代表该测试点总共有多少组测试数据,k的意义见 【问题描述】。
接下来t行每行两个整数n,m,其中n,m的意义见【问题描述】。
【输出格式】
t行,每行一个整数代表答案。
【样例输入】
【样例一】
1 2
3 3
【样例二】
1
【样例输出】
【样例一】
2 5
4 5
6 7
【样例二】
0
7
【数据范围】
1<=n,m<=2000,1<=t<=10000,1<=k<=21
【题解】
组合数求和公式Cnm=C(n-1)(m-1)+C(n-1)m
直接先预处理出所有数是不是k的倍数,然后再用二维前缀和就可以搞定了
【代码】
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
using namespace std;
#define ll long long
#define f(i,n) for(int i=1;i<=(n);i++)
#define MN 2010
#define N 2000
int read()
{
int x=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while( isdigit(c)){x=x*10+c-'0';c=getchar();}
return x*f;
}
int ans[MN][MN],dp[MN][MN];
int t,k,n,m;
int main()
{
t=read();k=read();
f(i,N)dp[i][0]=dp[0][i]=1;
f(i,N)for(int j=1;j<=i;j++)
{
dp[i][j]=(dp[i-1][j-1]+dp[i-1][j])%k;
if(!dp[i][j])ans[i][j]=1;
}
f(i,N)f(j,N)ans[i][j]+=ans[i-1][j]+ans[i][j-1]-ans[i-1][j-1];
while(t--)
{
n=read();m=read();
printf("%d
",ans[n][m]);
}
}