23.8.1 杭电暑期多校5部分题解

1005 - Snake

题目大意

你有 n n n 条长度为 1 1 1 的蛇,定义两条蛇可以进行对战并会使败方变成胜者的尾巴成为一条新的蛇,最后剩下 m m m 条蛇并且没有蛇的长度超过 k k k,问最后留下的蛇有几种不同情况

解题思路

很自然会从题目联想到 n n n 个球放 m m m 个盒子的方案数

当没有要求不能超过 k k k 时,可以用隔板法处理答案为 C n − 1 m − 1 C_{n-1}^{m-1} Cn1m1

因为 n n n 条蛇的前后顺序有关系,所以要乘 n ! n! n!

但这样这样最后会出现统计重复的情况,需要除以 m ! m! m!

既然要除去超过 k k k 的,可以考虑容斥,如果先在一里先放 k k k 个球,那么用隔板法处理后就会超过 k k k

那么可以求出有 i i i 个超过 k k k 的方案数为 n ! m ! ∗ C n − i ∗ k − 1 m − 1 ∗ C m i \frac{n!}{m!}*C_{n-i*k-1}^{m-1}*C_m^i m!n!Cnik1m1Cmi C m i C_m^i Cmi 表示 m m m 个盒子选 i i i 个先放 k k k

那么最后答案就是 ∑ i = 0 ( n − m ) / k ( − 1 ) i ∗ n ! m ! ∗ C n − i ∗ k − 1 m − 1 ∗ C m i \sum_{i=0}^{(n-m)/k}(-1)^i*\frac{n!}{m!}*C_{n-i*k-1}^{m-1}*C_m^i i=0(nm)/k(1)im!n!Cnik1m1Cmi

注意特判 m ∗ k < n m*kmk<n 的情况无解

code

#include 
using namespace std;
const int N = 1e6 + 9;
const int MOD = 998244353;
int t, n, m, k;
long long fac[N], inv[N];
long long pw(long long a, int b) {
	long long res = 1;
	while (b) {
		if (b & 1) res = res * a % MOD;
		a = a * a % MOD;
		b >>= 1;
	}
	return res;
}
long long cc(int n, int m) {return fac[n] * inv[m] % MOD * inv[n - m] % MOD;}
int main() {
	scanf("%d", &t);
	fac[0] = inv[0] = 1;
	for (int i = 1; i < N; ++ i) fac[i] = fac[i - 1] * i % MOD;
	inv[N - 1] = pw(fac[N - 1], MOD - 2);
	for (int i = N - 2; i; -- i) inv[i] = inv[i + 1] * (i + 1) % MOD;
	while (t --) {
		scanf("%d%d%d", &n, &m, &k);
		int opt = -1; long long sum = 0;
		if (m * k >= n) for (int i = 0; n - i * k >= m; ++ i)
			sum = (sum + (opt = -opt) * fac[n] * inv[m] % MOD * cc(n - i * k - 1, m - 1) % MOD * cc(m, i) % MOD + MOD) % MOD;
		printf("%lld\n", sum);
	}
	return 0;
} 

你可能感兴趣的:(组合数学)