组合数表示的是从 n 个物品中选出 m 个物品的方案数。举个例子,从 (1,2,3) 三个物品中选择两个物品可以有 (1,2),(1,3),(2,3) 这三种选择方法。
根据组合数的定义,我们可以给出计算组合数的一般公式:
Cnm=m!(n−m)!n!其中 n!=1×2×⋯×n。
小葱想知道如果给定 n,m 和 k,对于所有的 0≤i≤n,0≤j≤min(i,m) 有多少对 (i,j) 满足是 k 的倍数。
链接
LYOI#
题解
组合数递推,你们乐意叫Pascal定理就叫好了反正我叫杨辉三角。。。
Cnm=Cn−1m+Cn−1m−1递推过程中每次取模,若该位取模后为0则记该位答案为1,反之为0,对答案数组求二维前缀和,每次O(1)查询即可。
总复杂度O(2×20002+T)
代码
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int MAXN = 2000; int f[MAXN + 1][MAXN + 1], ans[MAXN + 1][MAXN + 1]; int main () { freopen("problem.in", "r", stdin); freopen("problem.out", "w", stdout); int t, k; scanf("%d %d", &t, &k); for (int i = 0; i <= MAXN; i++) f[i][0] = f[i][i] = 1; for (int i = 2; i <= MAXN; i++) { for (int j = 1; j < i; j++) { f[i][j] = (f[i - 1][j - 1] + f[i - 1][j]) % k; if (f[i][j] == 0) ans[i][j]++; } } for (int i = 1; i <= MAXN; i++) { for (int j = 1; j <= MAXN; j++) { ans[i][j] = ans[i - 1][j] + ans[i][j - 1] - ans[i - 1][j - 1] + ans[i][j]; } } while (t--) { int n, m; scanf("%d %d", &n, &m); printf("%d\n", ans[n][m]); } fclose(stdin); fclose(stdout); return 0; }
|