NOIP201608组合数问题

【问题描述】

组合数Cmn表示的是从n个物品中选出m个物品的方案数。举个例子,从(1,2,3)三个物品中选择两个物品可以有(1,2),(1,3),(2,3)这三种选择方法。根据组合数的定 义,我们可以给出计算组合数的一般公式:
Cnm=(n!m!)/(n−m)!
其中n! = 1 × 2 × · · · × n
小葱想知道如果给定n,mk,对于所有的0 <= i <= n,0 <= j <= min(i,m)有多少对(i,j)满足Cjik的倍数。

【输入格式】

第一行有两个整数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\n",ans[n][m]);
	}
}
posted @ 2017-11-10 14:42  qwerfcxs  阅读(291)  评论(0编辑  收藏  举报