D. Array Collapse
D. Array Collapse
You are given an array , where all elements are distinct.
You can perform several (possibly zero) operations with it. In one operation, you can choose a contiguous subsegment of and remove all elements from that subsegment, except for the minimum element on that subsegment. For example, if and you choose the subsegment from the -rd element to the -th element, the resulting array is .
An array is called reachable if it can be obtained from using several (maybe zero) aforementioned operations. Calculate the number of reachable arrays, and print it modulo .
Input
The first line of the input contains one integer () — the number of test cases.
Each test case consists of two lines. The first line contains one integer (). The second line contains distinct integers ().
Additional constraint on the input: the sum of over all test cases does not exceed .
Output
For each test case, print one integer — the number of reachable arrays, taken modulo .
Example
input
3
2
2 1
4
2 4 1 3
5
10 2 6 3 4
output
2
6
12
解题思路
容易知道剩余没被删除的元素必然构成原序列的一个子序列,因此问题等价于求合法(即按照题目要求删除)的子序列的数量。
求子序列的数量可以尝试用 dp。定义状态 表示以 结尾(没被删除)的合法子序列的数量。考虑可以从前面哪些状态 转移过来,也就是前面哪些元素可以作为这个子序列的倒数第二个元素。
假设在 前面比 的小的元素的最大下标是 ,那么下标满足 的元素都可以作为子序列的倒数第二个元素。只需删除区间 并留下最小的 ,那么 就自然变成了子序列的倒数第二个元素。
假设 前面比 小的元素的最大下标是 ,那么下标满足 的元素必然不可能作为子序列的倒数第二个元素。这是因为如果 要作为倒数第二个元素,那么必然要删除 ,而很明显只有删除区间的左端点小于等于 时才能把 删掉,与此同时 也会被删掉。另外可以发现 可作为子序列的倒数第二个元素,只需选择区间 删除即可。
同理假设 前面比 小的元素的最大下标是 ,那么下标满足 的元素必然不可能作为子序列的倒数第二个元素, 可作为子序列的倒数第二个元素。
可以发现 这些下标对应的元素依次递增,其实本质上是单调栈中的元素。为此我们用单调栈来维护这些可以转移到 的下标(状态),同时用一个变量来维护单调栈中所有状态 的总和 。当枚举到 ,通过弹出栈顶元素找到比 小的元素的下标,即此时栈顶的元素 ,那么就有 。其中 是 的前缀和,即 。另外如果栈为空即不存在比 小的元素,那么 还有加上 ,表示子序列只有 的情况。
最后答案就是单调栈中的状态的总和,即 。这是因为如果 要作为子序列的最后一个元素,那么 后面的元素都要被删除,即 是 中最小的元素。而单调栈中的元素就是满足该条件的 的下标。
AC 代码如下,时间复杂度为 :
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 3e5 + 10, mod = 998244353;
int a[N];
int f[N], s[N];
int stk[N], tp;
void solve() {
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", a + i);
}
tp = 0;
int sum = 0;
for (int i = 1; i <= n; i++) {
while (tp && a[stk[tp]] > a[i]) {
sum = (sum - f[stk[tp--]]) % mod;
}
f[i] = ((s[i - 1] - s[stk[tp]]) % mod + sum + !tp) % mod;
s[i] = (s[i - 1] + f[i]) % mod;
stk[++tp] = i;
sum = (sum + f[i]) % mod;
}
printf("%d\n", (sum + mod) % mod);
}
int main() {
int t;
scanf("%d", &t);
while (t--) {
solve();
}
return 0;
}
参考资料
单调栈优化 DP【Codeforces EDU 160 D】:https://www.bilibili.com/BV1v64y1p7pi/
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/17914646.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效