递推 数学

来源:互联网 发布:java速成教程 编辑:程序博客网 时间:2024/04/26 15:06

组合数问题

组合数 CmnCnm 表示的是从 nn 个物品中选出 mm 个物品的方案数。举个例子,从 (1,2,3)(1,2,3) 三个物品中选择两个物品可以有 (1,2),(1,3),(2,3)(1,2),(1,3),(2,3) 这三种选择方法。根据组合数的定义,我们可以给出计算组合数 CmnCnm 的一般公式:

Cmn=n!m!(nm)!Cnm=n!m!(n−m)!

其中 n!=1×2××nn!=1×2×⋯×n;特别地,定义 0!=10!=1

小葱想知道如果给定 n,mn,m 和 kk,对于所有的 0in,0jmin(i,m)0≤i≤n,0≤j≤min(i,m) 有多少对 (i,j)(i,j) 满足 CjiCij 是 kk 的倍数。

输入格式

从标准输入读入数据。

第一行有两个整数 t,kt,k,其中 tt 代表该测试点总共有多少组测试数据,kk 的意义见问题描述。

接下来 tt 行每行两个整数 n,mn,m,其中 n,mn,m 的意义见问题描述。

输出格式

输出到标准输出。

tt 行,每行一个整数代表所有的 0in,0jmin(i,m)0≤i≤n,0≤j≤min(i,m) 中有多少对 (i,j)(i,j) 满足 CjiCij 是 kk 的倍数。

样例一

input

1 23 3

output

1

explanation

在所有可能的情况中,只有 C12=2C21=2 是 22的倍数。

样例二

input

2 54 56 7

output

07


通过组合数的定义,可以发现组合数就是杨辉三角,

其中:
c[i][j] = c[i-1][j-1] + c[i-1][j]

从 i 个物品中取 j 个的方案数可以从已经计算过的 i - 1 来推出。

有一点特别需要注意,我们不可能这样推到 2000 去,而最终题目要求的也是在去模意义下的个数,所以我们采用边加边取模的方式,控制c[i][j]的范围。

通过: (a%p + b%p) % p = (a + b) % p 不断递推

然后求前缀和,每行相加就可以了。


Code
#include<iostream>#include<cstring>using namespace std;int t,k,n,m;int c[2005][2005];int s[2005][2005];int main(){memset(c,0,sizeof(c));memset(s,0,sizeof(0));cin >> t >> k;for(int i=0;i<2005;i++){c[i][0] = 1;c[i][i] = 1;}for(int i=2;i<2005;i++){for (int j=1;j<i;j++){c[i][j] = (c[i-1][j-1] + c[i-1][j]) % k;}}for(int i=0;i<2005;i++){if (c[i][0] == 0) s[i][0] = 1;for(int j=1;j<=i;j++){if (c[i][j] == 0) s[i][j] = s[i][j-1] + 1;else s[i][j] = s[i][j-1];}}for (int i=0;i<t;i++){cin >> n >> m;int ans = 0;for (int j=0;j<=n;j++){ans += s[j][min(j,m)];}cout << ans << endl;}return 0;}



0 0