CF 1913 D
题目描述
给定一个所有数互不相同的长度为 \(N\) 的序列 \(P\),你可以执行以下操作任意次:
- 选择一对 \(1\le l < r\le N\),并把其中除最小值外的所有元素删除。
求最终可以得到的不同序列数量。
思路
我们考虑怎样通过删除最少的元素来删除 \(i\),很明显,就是选择区间 \([l,i]\) 或 \([i,r]\),这里 \(l\) 是最大的满足 \(l<i且 P_l<P_i\) 的下标,而 \(r\) 是最小的满足 \(r>i且 P_r<P_i\) 的下标。这个可以使用单调栈求出。
我们每次都只删除这种最小区间,因为这样会使最终的方案更多。
令 \(dp_{0/1,i}\) 表示考虑前 \(i\) 个数,最后一个数选/不选的种类数。
我们有 \(dp_{0,i}\leftarrow dp_{0/1,l_i}\)(因为没有任何影响,随便怎么选,但如果不删除 \(i\),那么 \(l_i+1\) 到 \(i\) 都无法删除)。以及 \(dp_{1,i}\leftarrow dp_{1,j}(l_i\le j<i),dp_{0,l_i}\),因为删掉 \(i\) 就意味着删掉 \(l_i+1\) 到 \(i\),而 \(l_i\) 是既可以删也可以不删。这个可以使用前缀和优化。
时空复杂度均为 \(O(N)\)。
代码
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 300001, MOD = 998244353;
int t, n, a[MAXN], stk[MAXN], top, dp[2][MAXN], sum[MAXN];
void Solve() {
cin >> n;
for(int i = 1; i <= n; ++i) {
cin >> a[i];
}
top = 0;
dp[1][0] = sum[0] = 1;
for(int i = 1; i <= n; ++i) {
for(; top && a[stk[top]] >= a[i]; --top) {
}
int j = stk[top];
dp[0][i] = (j ? (dp[1][j] + dp[0][j]) % MOD : 0);
dp[1][i] = (0ll + dp[0][j] + sum[i - 1] - (j ? sum[j - 1] : 0) + MOD) % MOD;
sum[i] = (sum[i - 1] + dp[1][i]) % MOD;
stk[++top] = i;
}
cout << (dp[0][n] + dp[1][n]) % MOD << "\n";
}
int main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
for(cin >> t; t--; Solve()) {
}
return 0;
}