The 2021 CCPC 威海 M.810975 【容斥原理】
https://codeforces.com/gym/103428/problem/M
题意
有 \(n\) 场比赛,赢用 \(1\) 表示,输用 \(0\) 表示,需要求出有 \(m\) 场赢了,并且最多连赢的场次为 \(k\) 的方案数
思路
首先输的场数为 \(n-m\),那么就可以在 \(n-m+1\) 个位置里插入赢的 \(m\) 场比赛
不难想到方程:
\(x_1+x_2+...+x_{n-m+1}=m\),其中 \(0\leq x_i \leq k\)
要解方程,不难想到用容斥原理解决
第一种思路:
\(0\leq x_i \leq k\) 的反面转化成 \(x_i = k+1, k+2,...,m\),原方程就可以表示成 \(\sum_{j=1}^{n-m}x_j = m-x_i\),时间复杂度 \(O(n^2)\)
第二种思路:
\(0\leq x_i \leq k\) 的反面转化成 \(x_i \geq k + 1\),可以 \(O(n)\) 求出答案
具体来说,先求出 {$x_i \leq k $ } 的解(所有情况减去不合法情况),又因为其中包含了不满足最大值为 \(k\) 的情况,所以再减去 {$x_i \leq k-1 $ } 的方案数
式子推出来是:(\(C_n^{n-m}-(C_{n-m+1}^1*C_{n-(k+1)}^{n-m} - C_{n-m+1}^2*C_{n-2*(k+1)}^{n-m}+...)\)) - ...(\(C_n^{n-m}-(C_{n-m+1}^1*C_{n-k}^{n-m} - C_{n-m+1}^2*C_{n-2*k}^{n-m}+...)\))
code
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e5 + 10, mod = 998244353;
int n, m, k;
ll fac[N], inv[N];
ll quickpow(ll a, ll x) { //底数也开ll 因为可能是inv[]
ll ans = 1;
while(x) {
if(x & 1) {
ans *= a;
ans %= mod;
}
a *= a;
a %= mod;
x /= 2;
}
return ans;
}
void init(int n) {
fac[0] = 1;
for(int i = 1; i <= n; i++) {
fac[i] = fac[i - 1] * i % mod;
}
inv[n] = quickpow(fac[n], mod - 2);
for(int i = n - 1; i >= 1; i--) {
inv[i] = inv[i + 1] * (i + 1) % mod;
}
inv[0] = 1;
}
ll C(int n, int m) {
if(n < m) return 0;
return fac[n] * inv[m] % mod * inv[n - m] % mod;
}
inline ll solve(int k) {
ll ans = C(n, n - m);
for(int i = 1; i * k <= min(n, m); i++) {
if(i % 2) ans = (ans - C(n - m + 1, i) * C(n - k * i, n - m) % mod + mod) % mod;
else ans = (ans + C(n - m + 1, i) * C(n - k * i, n - m) % mod) % mod;
}
return ans;
}
int main() {
init(1e5);
scanf("%d%d%d", &n, &m, &k);
if(k == 0) {
if(m == 0) puts("1");
else puts("0");
system("pause");
return 0;
}
if(n < m || m < k) {
puts("0");
system("pause");
return 0;
}
printf("%lld\n", (solve(k + 1) - solve(k) + mod) % mod);
system("pause");
return 0;
}

浙公网安备 33010602011771号