CF 2066D2 Club of Young Aircraft Builders (hard version)
你怎么知道我 vp 虽然被 B 硬控了 50min 但最后在 1:59:50 绝杀 D2 了。(
说一个看起来暴力得多的做法!
考虑对于 \(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\) 层具体的分法,到阶段结束时再考虑计算贡献。
具体来说对于转移,考虑分为两种:
-
\(k + 1 < c\),那么直接飞一个纸飞机即可 \(f_{i, j, k}\to f_{i + 1, j, k + 1}\)。
-
\(k + 1 = c\),那么之后 \(j\) 这一层就不能飞纸飞机了,考虑枚举 \(j\) 这一层在之前飞了多少个纸飞机。
具体来说,记 \(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]\)。
\(k + 1 < c\) 时状态数 \(\mathcal{O}(nmc)\) 转移 \(\mathcal{O}(1)\),\(k + 1 = c\) 时状态数 \(\mathcal{O}(nm)\) 转移 \(\mathcal{O}(c)\),所以总时间复杂度为 \(\mathcal{O}(nmc)\)。
#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;
}
浙公网安备 33010602011771号