首页 > 其他分享 >D. Array Collapse

D. Array Collapse

时间:2023-12-19 20:37:25浏览次数:24  
标签:下标 Collapse int sum 元素 序列 Array mod

D. Array Collapse

You are given an array $[p_1, p_2, \dots, p_n]$, 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$ ($1 \le t \le 10^4$) — the number of test cases.

Each test case consists of two lines. The first line contains one integer $n$ ($1 \le n \le 3 \cdot 10^5$). The second line contains $n$ distinct integers $p_1, p_2, \dots, p_n$ ($1 \le p_i \le 10^9$).

Additional constraint on the input: the sum of $n$ over all test cases does not exceed $3 \cdot 10^5$.

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)$ 表示以 $a_i$ 结尾(没被删除)的合法子序列的数量。考虑可以从前面哪些状态 $f(j), \, j \in [i, i-1]$ 转移过来,也就是前面哪些元素可以作为这个子序列的倒数第二个元素。

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

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

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

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

  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/

标签:下标,Collapse,int,sum,元素,序列,Array,mod
From: https://www.cnblogs.com/onlyblues/p/17914646.html

相关文章

  • 浅析 ArrayList
    byemanjusakafromhttps://www.emanjusaka.top/2023/12/java-arrayList彼岸花开可奈何本文欢迎分享与聚合,全文转载请留下原文地址。ArrayList是一个使用List接口实现的Java类。顾名思义,JavaArrayList提供了动态数组的功能,其中数组的大小不是固定的。它实现了所有可选的列......
  • Queries for the Array 题解
    前言这场CF是我赛后打的,vp赛时没做出来,后来发现是有个地方理解错了,有一些细节没有考虑到。现在换了一种思路来写,感觉更清晰了。做法首先需要动态维护三个变量,\(cnt\)和\(finishsort\)和\(unfinishsort\)。这三个变量分别表示当前数字的个数,已经排好序的最后一个位置,和没......
  • C++ 反向遍历 array 小记
    有时候需要逆向循环,例如从字符串的最右端遍历到最左端,需要注意一些细节!初学遇到一些bug记录在这里。首先arr.size()的数据类型为size_t,为无符号整型对于for(intidx=arr.size()-1;idx>=0;idx--):使用int作为idx的类型,有一定概率会编译失败,因为size_t的具......
  • 关于奇怪的 Array 函数:
    关于奇怪的Array函数:众所周知,我们可以通过Array函数来做以下事情。初始化一个指定长度的数组。设置数组的初始值。//1.Initializeanarrayofthespecifiedlengthconstarray1=Array(3)//[,,]//2.Settheinitialvalueofthearrayconstarray2=......
  • Arrays工具类二分查找方法binarySearch方法详解​
    Arrays工具类二分查找方法binarySearch方法详解基础知识该方法的一般形式是publicstatic<T>intbinarySearch(T[]a,Tkey),对于基本类型,都有相应的重载方法,如针对int类型有binarySearch(int[]a,intkey)等。该方法的原理是使用二分查找算法在指定的数组中搜索指定的值。在调......
  • Java 数组和ArrayList排序
    数组排序1.数组排序(从小到大排序)importjava.util.Arrays;publicclassTest01{publicstaticvoidmain(String[]args){//数组(从小到大排序)//1.第一种方法Integer[]arr1={21,11,41,31,51};Arrays.sort(arr1);Sy......
  • Array数组常用方法
    <!DOCTYPEhtml><html><head><metacharset="utf-8"><title></title></head><body><script>vararr=[1,2,3,4]......
  • 无涯教程-Java - ByteArrayOutputStream函数
    ByteArrayOutputStream类流在内存中创建一个缓冲区,所有发送到该流的数据都存储在该缓冲区中。以下是ByteArrayOutputStream类将提供的构造函数的列表。Sr.No.Constructor&Remark1ByteArrayOutputStream()此构造函数创建一个具有32字节缓冲区的ByteArrayOutputStream。......
  • 无涯教程-Java - toCharArray()函数
    此方法将此字符串转换为新的字符数组。char[]toCharArray()-语法这是此方法的语法-publicchar[]toCharArray()char[]toCharArray()-返回值它返回一个新分配的字符数组,其长度是此字符串的长度,并且其内容已初始化为包含此字符串表示的字符序列。char[]toCharArray()......
  • Java 字符串、数组、ArrayList转换
    Java字符串、数组、ArrayList之间的相互转换 数组转字符串importjava.util.Arrays;publicclassTest02{publicstaticvoidmain(String[]args){int[]scores1=newint[]{10,20,30,40,50};int[]scores2={10,20,30,40,50};//数......