D. Array Collapse

D. Array Collapse

You are given an array [p1,p2,,pn], where all elements are distinct.

You can perform several (possibly zero) operations with it. In one operation, you can choose a contiguous subsegment of p and remove all elements from that subsegment, except for the minimum element on that subsegment. For example, if p=[3,1,4,7,5,2,6] and you choose the subsegment from the 3-rd element to the 6-th element, the resulting array is [3,1,2,6].

An array a is called reachable if it can be obtained from p using several (maybe zero) aforementioned operations. Calculate the number of reachable arrays, and print it modulo 998244353.

Input

The first line of the input contains one integer t (1t104) — the number of test cases.

Each test case consists of two lines. The first line contains one integer n (1n3105). The second line contains n distinct integers p1,p2,,pn (1pi109).

Additional constraint on the input: the sum of n over all test cases does not exceed 3105.

Output

For each test case, print one integer — the number of reachable arrays, taken modulo 998244353.

Example

input

3
2
2 1
4
2 4 1 3
5
10 2 6 3 4

output

2
6
12

 

解题思路

  容易知道剩余没被删除的元素必然构成原序列的一个子序列,因此问题等价于求合法(即按照题目要求删除)的子序列的数量。

  求子序列的数量可以尝试用 dp。定义状态 f(i) 表示以 ai 结尾(没被删除)的合法子序列的数量。考虑可以从前面哪些状态 f(j),j[i,i1] 转移过来,也就是前面哪些元素可以作为这个子序列的倒数第二个元素。

  假设在 ai 前面比 ai 的小的元素的最大下标是 x,那么下标满足 j[x,i1] 的元素都可以作为子序列的倒数第二个元素。只需删除区间 [j+1,i] 并留下最小的 ai,那么 aj 就自然变成了子序列的倒数第二个元素。

  假设 ax 前面比 ax 小的元素的最大下标是 y,那么下标满足 j[y+1,x1] 的元素必然不可能作为子序列的倒数第二个元素。这是因为如果 aj 要作为倒数第二个元素,那么必然要删除 ax,而很明显只有删除区间的左端点小于等于 y 时才能把 ax 删掉,与此同时 aj 也会被删掉。另外可以发现 ay 可作为子序列的倒数第二个元素,只需选择区间 [y,i1] 删除即可。

  同理假设 ay 前面比 ay 小的元素的最大下标是 z,那么下标满足 j[z+1,y1] 的元素必然不可能作为子序列的倒数第二个元素,az 可作为子序列的倒数第二个元素。

  可以发现 ,z,y,x,i 这些下标对应的元素依次递增,其实本质上是单调栈中的元素。为此我们用单调栈来维护这些可以转移到 f(i) 的下标(状态),同时用一个变量来维护单调栈中所有状态 f(j) 的总和 sum。当枚举到 ai,通过弹出栈顶元素找到比 ai 小的元素的下标,即此时栈顶的元素 t ,那么就有 f(i)=sist+sum。其中 sif(i) 的前缀和,即 si=j=1if(j)。另外如果栈为空即不存在比 ai 小的元素,那么 f(i) 还有加上 1,表示子序列只有 ai 的情况。

  最后答案就是单调栈中的状态的总和,即 sum。这是因为如果 ai 要作为子序列的最后一个元素,那么 ai 后面的元素都要被删除,即 aiaian 中最小的元素。而单调栈中的元素就是满足该条件的 ai 的下标。

  AC 代码如下,时间复杂度为 O(n)

#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/

posted @   onlyblues  阅读(139)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
Web Analytics
点击右上角即可分享
微信分享提示