「刷题记录」P2822 [NOIP2016 提高组] 组合数问题
本来以为很难,结果发现是道大水题
题目传送门:P2822 [NOIP2016 提高组] 组合数问题
思路:首先,你要知道什么是组合数,要知道组合数的递推公式 \(C^m_n = C^m_{n - 1} + C^{m - 1}_{n - 1}\),在本题中,你会发现 \(n, m \le 2000\),\(n\) 和 \(m\) 的范围都很小,所以我们可以直接暴力递推,但是可能会爆 long long我不知道,只是我的猜测,题中又提前给定了 \(k\),所以我们可以在递推的过程中边推边 \(\% k\),最后,只要模后是 \(0\),就说明这个可以被 \(k\) 整除,如果在从头到尾扫一遍记录有多少个零,那你会得到 \(90\),我们可以用前缀和优化,每行各记一个前缀和(为什么不直接二位前缀和?因为 \(\min(i, m)\)),上代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
inline ll read() {
ll x = 0;
int fg = 0;
char ch = getchar();
while (ch < '0' || ch > '9') {
fg |= (ch == '-');
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = (x << 3) + (x << 1) + (ch ^ 48);
ch = getchar();
}
return fg ? ~x + 1 : x;
}
const int N = 2e3 + 10;
int T, k, n, m, cnt;
ll C[N][N], sum[N][N];
void pre() {
for (int i = 0; i <= 2001; ++ i) {
C[i][0] = 1;
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 <= 2001; ++ i) {
for (int j = 1; j <= i; ++ j) {
sum[i][j] += sum[i][j - 1];
sum[i][j] += (C[i][j] == 0);
}
}
}
int main() {
T = read(), k = read();
pre();
while (T --) {
n = read(), m = read();
ll ans = 0;
for (int i = 0; i <= n; ++ i) {
ans += sum[i][min(i, m)];
}
printf("%lld\n", ans);
}
return 0;
}
真的大水题
朝气蓬勃 后生可畏