Loading

「刷题记录」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;
}

真的大水题

posted @ 2023-01-05 08:31  yi_fan0305  阅读(57)  评论(0编辑  收藏  举报