Loading

:D 获取中...

纪念逝去的 40pts

提供一种好想的优化方法。先拿出柿子(这里的 dfs(n, k) 相当于题解的 \({n \choose k} - z(n, k)\)):

int dfs(int n, int k) {
	if (n == k) return 0;
	if (n == 1) return 0;
	if (k == 1) return 0;
	if (~f[n][k]) return f[n][k];
	return f[n][k] = (dfs(n - 1, k) + dfs(n - 1, k - 1) + (n - k + 1 <= k)) % mod;
}

每次答案是 C(n, k) - dfs(n, k)。考虑一个 \((i, j)\) 数对对答案的贡献,只有在 \(i \neq 1, j \neq 1, i \neq j, i - j + 1 \leq j\) 时,它会贡献 \(1\),并依次往上记录到答案中。这个东西和组合数的递推方式非常像,此时 dfs(n, k) 计算中就会包含 \(n - i \choose k - j\)\((i, j)\) 数对,这就是它的贡献。

for (int i = 2; i <= n; i++)
	for (int j = max(k - n + i, i + 2 >> 1); j <= min(i - 1, k); j++)
		(ans += C(n - i, k - j)) %= mod; // k - n + i & k 的限制是为了 n - i >= k - j >= 0

然后你可以得到这样的东西来计算 dfs(n, k)。然后我们固定一下 \(j\),能贡献它的 \(i\) 是一段区间,可以使用变上限求和 \(\sum\limits_{i = 0} ^ n {i \choose k} = {n + 1 \choose k + 1}\) 来快速计算。

namespace liuzimingc {
#define pii pair<int, int>
#define endl '\n'

const int maxn = 1e7 + 5, mod = 998244353;

int T, n, k;
long long fac[maxn], inv[maxn];

int qkpow(long long a, int b) {
	long long ans = 1;
	while (b) {
		if (b & 1) ans = ans * a % mod;
		a = a * a % mod;
		b >>= 1;
	}
	return ans;
}

int C(int n, int m) {
	if (n < m) return 0;
	return fac[n] * inv[m] % mod * inv[n - m] % mod;
}

int get(int n, int k) {
	return C(n + 1, k + 1);
}

int get_sum(int l, int r, int k) {
	return (get(r, k) - get(l - 1, k) + mod) % mod;
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	fac[0] = 1;
	for (int i = 1; i <= 10000001; i++) fac[i] = fac[i - 1] * i % mod;
	inv[10000001] = qkpow(fac[10000001], mod - 2);
	for (int i = 10000000; ~i; i--) inv[i] = inv[i + 1] * (i + 1) % mod;
	cin >> T;
	while (T--) {
		cin >> n >> k;
		int ans = 0;
		for (int x = 0; x <= k; x++) {
			int j = k - x;
			int l = n - min(j + n - k, 2 * j - 1), r = n - (j + 1);
			if (l <= r) (ans += get_sum(l, r, x)) %= mod;
		}
		cout << (C(n, k) - ans + mod) % mod << endl;
	}
	return 0;
} 
#undef int
}
posted @ 2024-11-28 14:25  liuzimingc  阅读(42)  评论(2)    收藏  举报