题解:qoj9698 Twenty-two
题意:给出一个数列 \(a\),再给出若干次操作,每次操作形如以下两种:
-
所有数对 \(v\) 取 min。
-
区间对 \(v\) 取 max。
求最后的序列有多少种。
做法:
首先我们注意到一件事情,我们的原数组是不影响答案的,因为一个数要不然按自己算,要不然就是被取 max 后又取 min,或者你可以认为每个操作其实可以做无限次,但是其实有效的还是最后的,所以是不会影响最后的计数结果的,我们可以先认为所有数都是 0。
那么我们就考虑先把取 min 操作从小往大排,因为大的如果在小的前面完全没用,并且这么做也确实可以覆盖所有的操作方式,那么现在就等于我要把 max 操作插进去,每个数就变成了先对其取 max, 然后最后被取一次 min 状物的取值。
那么我们这里就可以开始考虑 dp 了,因为我们的更新都是一段段区间,所以我们考虑区间 dp,\(dp_{l,r,x}\) 代表我现在有的区间为 \([l,r]\),并且其中每个数都 \(\ge x\)。
如何转移呢?我们考虑先按值域扫描。考虑对于 \(\ge x+1\) 到 \(\ge x\) 如何转移,首先我们要判断一个区间能否被置成 \(x\),如果一个 max 区间能变成 x 是可以的,还有一种是我们通过全局取 min 做到的。
但是直接去通过枚举我们的区间去转移是困难的,因为你的区间有交集的情况相当难处理,我们考虑正难则反,考虑去处理有哪些位置不能有减掉。
首先考虑处理总的情况,我们直接认为目前怎么填都能有 \(x\),那么我们就等于要把 \(\ge x+1\) 的若干个区间拼在一起成为一个大的 \(\ge x\) 的区间即可,这个直接考虑最右端选的区间即可。
然后考虑如何减掉,我们先枚举左端点,讨论每一个更新操作,如果说我们有一个取 min 操作刚好为 \(x\),并且有一个 max 操作 \(\ge x\),那么我们就可以拉成 \(x\),如果没有取 min,但是刚好有一个 \(\max = x\) 的操作也是可行的,我们统计一下对于一个右端点,仅用这个右端点结束的区间最左到哪里。
然后我们再枚举区间的右端点,直接减会相当麻烦很容易重复,所以我们还是考虑枚举第一个合法区间,并且因为我们从小往大枚举右端点,在枚举的时候前面的贡献已经计算完了。
当我们扫到一个右端点的时候,我们将能满足 \(=x\) 的左端点到这个右端点之间全部打一个能被 cover 的标记,然后扫一遍在不能 cover 的地方减去即可,减去的代价即为左区间 \(\ge x\),右区间 \(\ge x\) 但是不一定满足的方案数。
给出代码,参考了 hos_lyric 的写法,使用左闭右开。
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 155, mod = 998244353;
int n, m, k, a[maxn], c[maxn], use[maxn], f[maxn][maxn], g[maxn][maxn];
int l[maxn], r[maxn], v[maxn], lx[maxn], cov[maxn];
signed main() {
cin >> n >> m >> k;
for (int i = 1; i <= n; i++)
cin >> a[i];
for (int i = 1; i <= m; i++)
cin >> c[i], use[c[i]] = 1;
sort(c + 1, c + m + 1);
for (int i = 1; i <= k; i++)
cin >> l[i] >> r[i] >> v[i];
for (int i = 0; i <= n; i++)
f[i][i + 1] = 1;
for (int i = n; i >= c[1]; i--) {
memset(g, 0, sizeof(g));
for (int l = n + 1; l >= 0; l--) {
g[l][l] = 1;
for (int r = l + 1; r <= n + 1; r++)
for (int t = l; t < r; t++)
g[l][r] = (g[l][r] + g[l][t] * f[t][r]) % mod;
}
memset(f, 0, sizeof(f));
for (int l1 = n; l1 >= 0; l1--) {
memset(lx, 0x3f, sizeof(lx));
for (int j = 1; j <= k; j++) {
if((use[i] && v[j] >= i) || v[j] == i) {
if(l[j] > l1)
lx[r[j]] = min(lx[r[j]], l[j]);
}
}
memset(cov, 0, sizeof(cov));
for (int r1 = l1 + 1; r1 <= n + 1; r1++) {
for (int t = lx[r1 - 1]; t < r1; t++)
cov[t] = 1;
f[l1][r1] = (f[l1][r1] + g[l1][r1]) % mod;
for (int t = l1 + 1; t < r1; t++)
if(!cov[t])
f[l1][r1] = (f[l1][r1] - f[l1][t] * g[t][r1] % mod + mod) % mod;
}
}
}
cout << g[0][n + 1] << endl;
return 0;
}

浙公网安备 33010602011771号