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;
}
posted @ 2024-09-17 18:00  Yaosicheng124  阅读(4)  评论(0编辑  收藏  举报