F. MEX vs MED

F. MEX vs MED

You are given a permutation $p_1,p_2, \dots ,p_n$ of length $n$ of numbers $0, \dots ,n−1$. Count the number of subsegments $1 \leq l \leq r \leq n$ of this permutation such that $mex(p_l, p_{l+1}, \ldots, p_r) > med(p_l, p_{l+1}, \ldots, p_r)$.

$mex$ of $S$ is the smallest non-negative integer that does not occur in $S$. For example:

  • $mex({0, 1, 2, 3}) = 4$
  • $mex({0, 4, 1, 3}) = 2$
  • $mex({5, 4, 0, 1, 2}) = 3$

$med$ of the set $S$ is the median of the set, i.e. the element that, after sorting the elements in non-decreasing order, will be at position number $\left \lfloor{ \frac{|S| + 1}{2} } \right \rfloor$ (array elements are numbered starting from $1$ and here $\left \lfloor{v} \right \rfloor$ denotes rounding $v$ down.). For example:

  • $med({0, 1, 2, 3}) = 1$
  • $med({0, 4, 1, 3}) = 1$
  • $med({5, 4, 0, 1, 2}) = 2$

A sequence of $n$ numbers is called a permutation if it contains all the numbers from $0$ to $n−1$ exactly once.

Input

The first line of the input contains a single integer $t$ $(1 \leq t \leq {10}^{4})$, the number of test cases.

The descriptions of the test cases follow.

The first line of each test case contains a single integer $n$ $(1 \leq n \leq 2 \cdot {10}^{5})$, the length of the permutation $p$.

The second line of each test case contains exactly $n$ integers: $p_1,p_2, \dots ,p_n$ $(0 \leq p_i \leq n−1)$, elements of permutation $p$.

It is guaranteed that the sum of $n$ over all test cases does not exceed $2 \cdot {10}^{5}$.

Output

For each test case print the answer in a single line: the number of subsegments $1 \leq l \leq r \leq n$ of this permutation such that $mex(p_l, p_{l+1}, \ldots, p_r) > med(p_l, p_{l+1}, \ldots, p_r)$.

Example

input

8
1
0
2
1 0
3
1 0 2
4
0 2 1 3
5
3 1 0 2 4
6
2 0 4 1 3 5
8
3 7 2 6 0 1 5 4
4
2 0 1 3

output

1
2
4
4
8
8
15
6

Note

The first test case contains exactly one subsegment and $mex({0}) = 1 > med({0}) = 0$ on it.

In the third test case, on the following subsegments: $[1,0]$, $[0]$, $[1,0,2]$ and $[0,2]$, $mex$ is greater than $med$.

In the fourth test case, on the following subsegments: $[0,2]$, $[0]$, $[0,2,1]$ and $[0,2,1,3]$, $mex$ greater than $med$.

 

解题思路

  对于这种统计类的问题关键在于如何划分集合使得计数的过程能够做到不重不漏。一般来对于一个序列我们会根据右端点来划分集合,然后枚举右端点来求解满足要求的答案。这里的话可以发现枚举右端点是不行的,因为整个过程的时间复杂度会比较高。

  因此这里要选择另外的方式来划分集合。我们根据$\text{mex}$来划分集合。根据定义,如果某个序列的$\text{mex} = i$,那么这个序列必须包含$0 \sim i - 1$这些值,并且不能包含$i$。因为整个序列是一个排列,因此我们可以记录每个数字的下标位置$\text{p[i]}$,然后预处理出$0 \sim i$所有的数值前缀的最小下标和最大下标,记为$\text{minp}[i]$和$\text{maxp}[i]$。其中$\text{minp}[i]$表示数字$0 \sim i$中最小的下标位置,$\text{maxp}[i]$表示数字$0 \sim i$中最大的下标位置。因此对于某个$\text{mex} = i$,应该满足$\text{p}[i] < \text{minp}[i-1] \,\,\text{and}\,\, \text{p}[i] > \text{maxp}[i-1]$。

  现在要找出所有$\text{mex} = i$的区间,看看有多少个区间满足$\text{mex} > \text{med}$。可以发现在$\text{mex} = i$的区间中,必定包含$0 \sim i - 1$(一共$i$个元素),其余的元素都是严格大于$i$的,如果区间长度不超过$2i$,那么这个区间的$\text{med}$必定小于$i$,如果区间长度超过$2i$,那么$\text{med}$必定大于$i$。只要区间的长度不超过$2i$,那么就是符合条件的答案。

  即现在我们要枚举$\text{mex}$值,找到包含数字$0 \sim i - 1$的长度最小的区间(即区间$\left[ \text{minp}[i], \text{maxp}[i] \right]$),然后根据左右端点往外延拓,统计包含这个最小长度的区间,且区间长度不超过$2i$的区间数目(这里的统计就是根据端点来划分集合了)。这里往外延拓既可以选择枚举右端点求满足条件的左端点数目,也可以枚举左端点求满足条件的右端点数目。

  考虑一种情况,如果已经找到了$\text{mex} = i$的最小区间$[l, r]$,并且$\text{p}[i] < l$,我们都选择枚举右端点求左端点数目。然后$i + 1$又在$i$的左边,此时$\text{mex} = i + 1$的最小区间为$[\text{p}[i], r]$,继续选择枚举右端点求左端点数目。如果之后的数字都出现在上一个数字的左边,又因为我们每次都从$r$往后枚举,因此时间复杂度会变成$O(n^2)$。这意味着我们要选择合理的枚举方式。

  具体来说是这样的,对于$\text{mex} = i$的最小区间$[l, r]$,如果$\text{p}[i] > r$,即$i$在区间的右边,这时我们应该枚举右端点求满足条件的左端点数目。如果如果$\text{p}[i] < l$,即$i$在区间的左边,这时我们应该枚举左端点求满足条件的右端点数目。这样我们就可以遍历的过程中不重复地枚举$n$个位置(因为枚举的位置下一次就变成$\text{mex} = i+1$的最小区间,即每次枚举的位置都会变少),即枚举的次数总和就是序列的总长度,因此整个时间复杂度为$O(n)$。

  AC代码如下:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 typedef long long LL;
 5 
 6 const int N = 2e5 + 10;
 7 
 8 int p[N], minp[N], maxp[N];
 9 
10 void solve() {
11     int n;
12     scanf("%d", &n);
13     for (int i = 0; i < n; i++) {
14         int x;
15         scanf("%d", &x);
16         p[x] = i;
17     }
18     
19     p[n] = n;    // mex = n,按理来说n不会出现在序列中,因此我们把n定义在序列之外
20     minp[0] = maxp[0] = p[0];    // 初始时数字0的就是一个区间
21     for (int i = 1; i < n; i++) {
22         minp[i] = min(minp[i - 1], p[i]);    // 求数字1~i的最小下标
23         maxp[i] = max(maxp[i - 1], p[i]);    // 求数字1~i的最大下标
24     }
25     
26     LL ret = 0;
27     for (int i = 1; i <= n; i++) {    // mex = 0时,没有满足要求的区间,因此从mex = 1开始
28         if (p[i] >= minp[i - 1] && p[i] <= maxp[i - 1]) continue;    // i在最小区间范围内
29         int len = 2 * i;
30         if (p[i] < minp[i - 1]) {    // i在最小区间左边
31             for (int j = minp[i - 1]; j > p[i]; j--) {    // 枚举左端点
32                 if (j + len - 1 < maxp[i - 1]) break;    // 区间[j, j + len - 1]没有覆盖最小区间
33                 ret += min(n - 1, j + len - 1) - maxp[i - 1] + 1;    // 左端点固定,满足条件的右端点范围就在[maxp[i-1], j+len-1]
34             }
35         }
36         else {
37             for (int j = maxp[i - 1]; j < p[i]; j++) {    // 枚举左端点
38                 if (j - len + 1 > minp[i - 1]) break;    // 区间[j - len + 1, j]没有覆盖最小区间
39                 ret += minp[i - 1] - max(0, j - len + 1) + 1;    // 右端点固定,满足条件的左端点范围就在[j-len+1, minp[i-1]]
40             }
41         }
42     }
43     
44     printf("%lld\n", ret);
45 }
46 
47 int main() {
48     int t;
49     scanf("%d", &t);
50     while (t--) {
51         solve();
52     }
53     
54     return 0;
55 }

  2023-02-13更新:修正了部分错误,贴出一份新AC代码:

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;

const int N = 2e5 + 10;

int p[N], minp[N], maxp[N];

void solve() {
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        int x;
        scanf("%d", &x);
        p[x] = i;
    }
    minp[0] = maxp[0] = p[0];
    for (int i = 1; i < n; i++) {
        minp[i] = min(minp[i - 1], p[i]);
        maxp[i] = max(maxp[i - 1], p[i]);
    }
    LL ret = 1;
    for (int i = 1; i < n; i++) {
        if (p[i] < minp[i - 1] || p[i] > maxp[i - 1]) {
            int len = 2 * i;
            if (p[i] < minp[i - 1]) {
                for (int j = minp[i - 1]; j > p[i]; j--) {
                    if (j + len - 1 < maxp[i - 1]) break;
                    ret += min(n- 1, j + len - 1) - maxp[i - 1] + 1;
                }
            }
            else {
                for (int j = maxp[i - 1]; j < p[i]; j++) {
                    if (j - len + 1 > minp[i - 1]) break;
                    ret += minp[i - 1] - max(0, j - len + 1) + 1;
                }
            }
        }
    }
    printf("%lld\n", ret);
}

int main() {
    int t;
    scanf("%d", &t);
    while (t--) {
        solve();
    }
    
    return 0;
}
View Code

 

参考资料

  Codeforces Round #828 (Div. 3) E, F:https://zhuanlan.zhihu.com/p/574230639

posted @ 2022-10-26 11:35  onlyblues  阅读(74)  评论(0编辑  收藏  举报
Web Analytics