【NOIP 2016 提高组】组合数问题
【题目】
题目描述:
组合数 表示的是从 个物品中选出 个物品的方案数。举个例子,从 三个物品中选择两个物品可以有 ,, 这三种选择方法。根据组合数的定义,我们可以给出计算组合数 的一般公式:
其中 ;特别地,定义 。
小葱想知道如果给定 , 和 ,对于所有的 , 有多少对 满足 是 的倍数。
输入格式:
第一行有两个整数 ,,其中 代表该测试点总共有多少组测试数据, 的意义见【问题描述】。
接下来 行每行两个整数 ,,其中 , 的意义见【问题描述】。
输出格式:
输出 行,每行一个整数代表所有的 , 中有多少对 满足 是 的倍数。
样例数据:
【样例】
输入
1 2
3 3
输出
1
【样例】
输入
2 5
4 5
6 7
输出
0
7
备注:
【样例说明】
在所有可能的情况中,只有 是 的倍数。
【数据规模与约定】
【分析】
90 pts:
妙啊,暴力 分啊
其实在考 的时候, 分就约等于满分了吧,反正如果我 有 分了,肯定就把更多的时间花在 了
要得到这 分,我们需要知道一个东西,即
是不是很眼熟,对,这就是杨辉三角的递推公式啊
所以我们预处理出 以内的所有组合数(边做边取模),然后用 O()查询就行了
100 pts:
我离 掉 只差一个前缀和。。。
由于每次询问都是从 开始的,而且没有修改,就想到前缀和
就是用 表示对于询问 的答案(也是预处理出来的)
更新的时候,就是
如果此时 , 就加
(实在不太理解就画个图吧,画个图应该就明白了)
然后实际操作的时候注意一下边界的细节问题就行了
【代码】
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 2005
using namespace std;
int n,m,t,k;
int C[N][N],ans[N][N];
void init()
{
int i,j;
C[0][0]=1;
for(i=1;i<=2000;++i)
{
C[i][0]=1;
for(j=1;j<=i;++j)
{
C[i][j]=(C[i-1][j-1]+C[i-1][j])%k;
ans[i][j]=ans[i-1][j]+ans[i][j-1]-ans[i-1][j-1];
if(!C[i][j]) ans[i][j]++;
}
ans[i][i+1]=ans[i][i];
}
}
int main()
{
scanf("%d%d",&t,&k);
init();
while(t--)
{
scanf("%d%d",&n,&m);
printf("%d\n",m>n?ans[n][n]:ans[n][m]);
}
return 0;
}