组合意义可以考虑我们先将 a 个球装到对应的 i 个盒子里,然后将剩下的 m - a 个球装到剩下的 i + 1 个盒子里,每个盒子对应的限制如上所示。
那么奇数的连续段数为 i 时的方案数就是:
{a - 1 \choose i - 1} \times {m - a \choose i}
同理可得奇数的连续段数为 i,选择奇数 1 时的方案数为:
{a - 1 \choose i - 1} \times {m - a \choose i - 1}
接下来就是解决当连续段个数为 i 时,偶数能选的位置个数为多少,这个是简单的:
当不选择奇数 1 时,个数为: m - a - i。
当选择奇数 1 时,个数为:m - a - i + 1。
然后答案即为:
\sum_{i = 1} ^ a {m - a \choose i} \times {a - 1 \choose i - 1} \times {m - a - i \choose b} + \sum_{i = 1} ^ n {m - a \choose i - 1} \times {a - 1 \choose i - 1} \times {m - a - i + 1 \choose b}
直接计算的复杂度是 \Theta(Ta) 的,期望得分 50\ \text{pts}。
我们考虑推式子来优化,这里以前半部分为例,后半部分同理:
\sum_{i = 1} ^ a {m - a \choose i} \times {a - 1 \choose i - 1} \times {m - a - i \choose b}
相信在座的各位 ikun 肯定做过 P2791 幼儿园篮球题,这道题和那道题的套路很像。
发现什么都看不出来,于是把组合数拆成阶乘的形式:
\sum_{i = 1} ^ a {a - 1 \choose i - 1} \times \dfrac{(m - a)! \times (m - a - i)!}{i! \times (m - a - i)! \times b! \times (m - a - i - b)!}
然后发现 (m - a - i)! 可以约掉:
\sum_{i = 1} ^ a {a - 1 \choose i - 1} \times \dfrac{(m - a)!}{i! \times b! \times (m - a - i - b)!}
发现分母中有 i! 和 (m - a - i - b)!,要是分子中有 (m - a - b)! 就能凑成组合数了,于是分式上下同乘 (m - a - b)!。
\sum_{i = 1} ^ a {a - 1 \choose i - 1} \times \dfrac{(m - a)! \times (m - a - b)!}{i! \times b! \times (m - a - i - b)! \times (m - a - b)!}
然后把组合数再提出来:
\sum_{i = 1} ^ a {a - 1 \choose i - 1} \times {m - a - b \choose i} \times {m - a \choose b}
我们惊喜地发现最后一项是和 i 无关的,于是把它提到前面:
{m - a \choose b} \times \sum_{i = 1} ^ a {a - 1 \choose i - 1} \times {m - a - b \choose i}
由于:
{a - 1 \choose i - 1} = {a - 1 \choose a - i}
所以可得原式等于:
{m - a \choose b} \times \sum_{i = 1} ^ a {a - 1 \choose a - i} \times {m - a - b \choose i}
然后就发现后面的式子可以范德蒙德卷积了:
{m - a \choose b} \times {m - b - 1 \choose a}
选择奇数 1 的情况同理,最终推出式子为:
{m - a \choose b} \times {m - b - 1 \choose a} + {m - a \choose b} \times {m - b - 1 \choose a - 1}
至此,时间复杂度优化为 \Theta(T + m),期望得分 100\ \text{pts}。
代码实现
#include <bits/stdc++.h>
using namespace std;
constexpr int mod = 998244353;
int fac[5000005], ifac[5000005];
int qpow(int x, int p) {
int ans = 1;
while (p) {
if (p & 1) ans = 1ll * ans * x % mod;
p >>= 1, x = 1ll * x * x % mod;
}
return ans;
}
int C(int m, int n) {
if (n > m or n < 0 or m < 0) return 0;
return 1ll * fac[m] * ifac[n] % mod * ifac[ m - n ] % mod;
}
int main() {
ios :: sync_with_stdio(0), cin.tie(0);
fac[0] = 1;
for (int i = 1; i <= 5e6; ++i) fac[i] = 1ll * fac[ i - 1 ] * i % mod;
ifac[5000000] = qpow(fac[5000000], mod - 2);
for (int i = 5e6; i; --i) ifac[ i - 1 ] = 1ll * ifac[i] * i % mod;
int t; cin >> t;
while (t--) {
int a, b, m; cin >> m >> a >> b;
cout << 1ll * C(m - a, b) * (C(m - b - 1, a) + C(m - b - 1, a - 1)) % mod << '\n';
}
return 0;
}