CF 2066D2 Club of Young Aircraft Builders (hard version)
rizynvu
·
2025-07-10 18:53:38
·
题解
cnblogs。
说一个看起来暴力得多的做法!
考虑对于 i 只有在 i\sim n 层飞出的纸飞机数量 < c 时才能飞一个纸飞机。
那么如果知道了当前每个楼层飞出的纸飞机数,考虑找到最小的 i 满足 i\sim n 层飞出的纸飞机数 < c ,此时就相当于是只有 i\sim n 层可以飞出纸飞机。
发现这就天然的分出了阶段:1\sim n 层都可以飞纸飞机,2\sim n 层可以飞纸飞机,一直到最后第 n 层的纸飞机也飞完结束。
于是考虑记 f_{i, j, k} 表示考虑了前 i 个纸飞机,此时 j\sim n 层还可以飞纸飞机,且 j\sim n 层一共飞了 k 个纸飞机的方案数。
为了方便转移,考虑延后计算贡献,就是此时不去区分 j\sim n 层具体的分法,到阶段结束时再考虑计算贡献。
具体来说对于转移,考虑分为两种:
具体来说,记 $1\sim i$ 中 $a_{i'} > j$ 的数量为 $c_1$,$a_{i'} = j$ 的数量为 $c_2$,那么还有 $k - c_1 - c_2$ 个纸飞机没有确定,枚举 $x\in [c_2. k - c_1]$ 表示 $j$ 层的纸飞机数,转移为 $\binom{k - c_1 - c_2}{x - c_2}f_{i, j, k}\to f_{i + 1, j + 1, k - x}$。
需要注意可能 $j$ 这一层飞出去了 $0$ 个纸飞机,所以 $j + 1\sim n$ 的纸飞机数仍为 $k$,要继续删掉 $j + 1$ 层。
还需要考虑的是,如果遇到了 a_i > 0 ,说明此时 a_i 一定能够飞纸飞机,所以 j 的合法范围应为 [1, a_i] 。
```cpp
#include <bits/stdc++.h>
using ll = long long;
constexpr ll mod = 1e9 + 7;
constexpr int maxn = 100 + 2;
constexpr int maxm = 10000 + 5;
ll binom[maxn][maxn];
inline void init() {
for (int i = 0; i < maxn; i++) {
binom[i][0] = 1;
for (int j = 1; j <= i; j++) {
binom[i][j] = (binom[i - 1][j - 1] + binom[i - 1][j]) % mod;
}
}
}
int a[maxm], b[maxn], sum[maxm][maxn];
ll f[maxn][maxn], g[maxn][maxn];
inline void solve() {
int n, c, m;
scanf("%d%d%d", &n, &c, &m);
for (int i = 1; i <= m; i++) scanf("%d", &a[i]);
for (int i = 1; i <= n; i++) {
b[i] = std::count(a + 1, a + m + 1, i);
if (b[i] > c) {
return puts("0"), void();
}
}
for (int i = 1; i <= m; i++) {
memcpy(sum[i], sum[i - 1], sizeof(sum[i]));
for (int j = 1; j <= a[i]; j++) {
sum[i][j]++;
}
}
memset(f, 0, sizeof(f)), f[1][0] = 1;
for (int i = 1; i <= m; i++) {
for (int p = 1; p <= (a[i] == 0 ? n : a[i]); p++) {
for (int x = c; x >= 1; x--) {
f[p][x] = f[p][x - 1];
}
f[p][0] = 0;
}
for (int p = (a[i] == 0 ? n : a[i]) + 1; p <= n + 1; p++) {
memset(f[p], 0, sizeof(ll) * (c + 1));
}
for (int p = 1; p <= n; p++) {
for (int x = b[p]; x <= c - sum[i][p + 1]; x++) {
(f[p + 1][c - x] += f[p][c] * binom[c - sum[i][p]][x - b[p]]) %= mod;
}
f[p][c] = 0;
}
}
printf("%lld\n", f[n + 1][0]);
}
int main() {
init();
int t; scanf("%d", &t);
while (t--) solve();
return 0;
}
```