题解:AT_utpc2023_p Priority Queue 3
Priestess_SLG · · 题解
为了方便,这里把
设在某一时刻当前还没有被弹出的
如果某一时刻 - 操作之前堆中有 - 操作弹出的元素必然属于
考虑维护当前时刻的
具体的:设
初始条件显然就是
然后考虑如果当前遇到了一个 + 操作,则先设前 + 操作和 - 操作,则当前
考虑如果插入非
如果当前插入
如果当前插入的值恰好不是
然后考虑如果当前遇到了一个 - 操作,则此时因为弹出的元素必须属于
同样考虑分类讨论:
如果当前弹出的元素不是
- 如果当前
o=0 ,则A_j 根本就不在堆里,当然不会弹出A_j 元素。 - 否则,如果
o=1\land k>1 则堆里除了A_j 以外还有其他属于A 集合内的元素,她们全都小于A_j 因此小根堆里肯定会先弹出其他元素。
总结一下,如果当前
如果当前弹出的值是 - 操作一定必须把
考虑之前一共有
直接按照上述 dp 转移时间时间复杂度看起来都是
:::success[Code] 代码中部分变量名和上文不同。
namespace lowspeed_song {
int fac[N], inv[N], ifac[N];
inline void init() {
fac[0] = inv[0] = ifac[0] = 1;
fac[1] = inv[1] = ifac[1] = 1;
for (int i = 2; i < N; ++i) {
fac[i] = fac[i - 1] * i % mod;
inv[i] = mod - inv[mod % i] * (mod / i) % mod;
ifac[i] = ifac[i - 1] * inv[i] % mod;
}
}
inline int binom(int a, int b) {
if (b < 0 || a < b) return 0;
return fac[a] * ifac[b] % mod * ifac[a - b] % mod;
}
char s[610];
int f[2][310][310][2], a[310];
inline void sol([[maybe_unused]]int __testcase_id) {
int n, m, q = 0; cin >> n >> m;
scanf("%s", s);
for (int i = 0; i < m; ++i) cin >> a[i];
sort(a, a + m, greater<>());
f[1][0][0][0] = 1;
for (int i = 0; i < n + m; ++i) {
memset(f[i & 1], 0, sizeof f[i & 1]);
for (int j = 0; j <= m; ++j)
for (int k = 0; k <= m; ++k)
for (int o : {0, 1}) {
if (s[i] == '+') {
if (n - a[j] - j - i + q + q + k > 0) f[i & 1][j][k][o] = (f[i & 1][j][k][o] + (n - a[j] - j - i + q + q + k) * f[~i & 1][j][k][o]) % mod;
if (j < m && !o && m - q - k > 0) f[i & 1][j][k + 1][1] = (f[i & 1][j][k + 1][1] + f[~i & 1][j][k][o]) % mod;
if (m - q - k + o > 1) f[i & 1][j][k + 1][o] = (f[i & 1][j][k + 1][o] + f[~i & 1][j][k][o]) % mod;
} else if (k) {
if (k != 1 || o != 1) f[i & 1][j][k - 1][o] = (f[i & 1][j][k - 1][o] + f[~i & 1][j][k][o]) % mod;
else for (int p = 0; p <= min(q - j, m - j - 1); ++p) f[i & 1][j + p + 1][0][0] = (f[i & 1][j + p + 1][0][0] + f[~i & 1][j][k][o] * fac[q - j] % mod * ifac[q - j - p] % mod) % mod;
}
}
if (s[i] == '-') ++q;
} cout << f[~(n + m) & 1][m][0][0] << '\n';
}
} // namespace lowspeed_song
:::