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*k<n mk<n 的情况无解

code

#include <bits/stdc++.h>
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;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值