考虑转化一下,每个最后留下来的集合都相对的对应着一个被删除的集合。
于是考虑去对被删除的数去计数。
然后贪心的,去让每一次 \(2\) 操作删除的数都是前面加入中还剩下的最后加入的数(因为有的可能被前面的 \(2\) 操作删了)。
对于证明,考虑到如果不是剩下的最后加入的,那么中间可能会有 \(2\) 操作截胡了,且把其调整到最后加入肯定不劣。
于是可以先把这些已经确定要被删去的 \(1\) 操作删掉,被这个操作一起考虑到 \(2\) 操作上。
那么此时 \(2\) 操作的定义就是把一个大于当前已有的数的集合的没选过的数加入到删除的集合里。
于是可以知道对于一个 \(2\) 操作,其选的数一定会大于前面每个 \(1\) 操作选的数。
但是此时 \(2\) 操作之间的大小关系还没确定,还是不是很好做。
但是,凭直觉去想一想,可以知道同样可以贪心的,让后面的 \(2\) 选的数大于前面的 \(2\) 选的数。
证明考虑记第 \(i\) 次 \(2\) 操作选到的数为 \(s_i\),那么如果有 \(i < j, s_i > s_j\),那么肯定 \(j\) 之前选的 \(1\) 都没有 \(s_j\) 大,因为 \(i < j\) 于是让 \(i\) 位置选 \(s_j\) 肯定满足,因为又有 \(s_i > s_j\) 所以 \(j\) 位置选 \(s_i\) 肯定也满足。
于是可以知道 \(2\) 操作选的数需要大于所有前面操作选的数。
记第 \(i\) 次 \(2\) 操作在操作序列中是第 \(l_i\) 个,所以可以知道这次操作选的数的区间就是 \([l_i, a]\)。
同时又有 \(2\) 操作选的数是递增的,于是考虑 DP。
设 \(f_{i, j}\) 为第 \(i\) 此 \(2\) 操作选的数为 \(j\) 的方案数,那么有转移 \(f_{i, j} = \begin{cases} 0 & j < l_i\\ \sum\limits_{k = 0}^{j - 1} f_{i - 1, k} & j \ge l_i\end{cases}\)。
然后用个前缀和优化就可以了。
时间复杂度 \(\mathcal{O}(ab)\)。
#include<bits/stdc++.h>
constexpr int mod = 998244353;
const int maxn = 1e4 + 10;
int x[maxn], l[maxn];
int f[maxn];
int main() {
int a, b;
scanf("%d%d", &a, &b);
for (int i = 1; i <= a + b; i++)
scanf("%d", &x[i]);
for (int i = a + b, tot = 0; i; i--) {
if (x[i] == 2) tot++;
else if (tot) x[i] = 0, tot--;
}
for (int i = 1, tot = 0, n = 0; i <= a + b; i++) {
tot += x[i] > 0;
if (x[i] == 2) l[++n] = tot;
}
for (int i = 0; i <= a; i++)
f[i] = 1;
for (int i = 1; i <= b; i++) {
for (int j = a; j >= l[i]; j--)
f[j] = f[j - 1];
for (int j = l[i] - 1; ~ j; j--)
f[j] = 0;
for (int j = 1; j <= a; j++)
(f[j] += f[j - 1]) %= mod;
}
printf("%d\n", f[a]);
return 0;
}
标签:Atcoder,于是,int,前面,Queue,Priority,maxn,操作,考虑
From: https://www.cnblogs.com/rizynvu/p/18298459