组合数问题具体描述:
组合数Cmn表示从n个物品中选m个物品的方案数,根据组合数的定义,我们可以知道计算组合数的一般公式:
Cmn=n!/(m!*(n-m)!)
现在,小葱想知道如果给的n,m,k, 对于0<=i<=n, 0<=j<=min(i,m),有多少对(i,j)满足Cji是k的倍数
输入描述 Input Description
第一行有两个整数t, k,其中t代表该测试点总共有多少组测试数据,k的意义见【问题描述】。
接下来t行每行两个整数n, m,其中n, m的意义见【问题描述】。
输出描述 Output Description
t行,每行一个整数代表所有的0<=i<=n,0<=j<=min(i,m)中有多少对(i, j)满足C(j,i)是k的倍数
样例输入 Sample Input
输入1:
1 2
3 3
输入2:
2 5
4 5
6 7
样例输出 Sample Output
输出1:
1
输出2:
0
7
数据范围及提示 Data Size & Hint
样例1提示:
在所有可能的情况中,只有C(1,2)是2的倍数。
n<=2000
m<=2000
k<=21
t<=10^4
闲话
去年考NOIP的时候还是一个只学了两个月的傻逼...当时看到这道题直接打了暴力...话说当时没学多少竟然还混了个二等奖.不过现在反观去年NOIP真的比起前几年难度是猛增啊, D1T2竟然才是最难题.去年好像是第一次出现了期望dp和状压dp...难不成今年就要考轮廓线和插头了吗...
题解
其实就是求出n,m内的组合数和, 因为毕竟由线性递推公式处理组合数之后, 每次模的树就是k, 看一下每个是不是0即可, 考虑到多组数据, 离线处理线性递推式(就是杨辉三角), 然后再处理前缀和优化即可.
#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn = 2005;
int k, T, maxx, ans;
int n[maxn*5], m[maxn*5], c[maxn][maxn], sum[maxn][maxn];
int main(){
scanf("%d%d", &T, &k);
for(register int i = 1; i <= T; ++i){
scanf("%d%d", &n[i], &m[i]);
maxx = max(maxx, n[i]);
}
for(register int i = 0; i <= maxx; ++i) c[i][i] = 1, c[i][1] = i % k;
for(register int i = 2; i <= maxx; ++i)
for(register int j = 2; j <= maxx; ++j)
c[i][j] = (c[i-1][j-1] + c[i-1][j]) % k;
for(register int j = 1; j <= maxx; ++j)
for(register int i = j; i <= maxn; ++i)
if(!c[i][j])
sum[i][j] = sum[i-1][j] + 1;
else sum[i][j] = sum[i-1][j];
for(register int t = 1; t <= T; ++t){
ans = 0;
for(register int i = 1; i <= m[t]; ++i)
ans += sum[n[t]][i];
printf("%d\n", ans);
}
}