F. MEX vs MED

F. MEX vs MED

You are given a permutation p1,p2,,pn of length n of numbers 0,,n1. Count the number of subsegments 1lrn of this permutation such that mex(pl,pl+1,,pr)>med(pl,pl+1,,pr).

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 |S|+12 (array elements are numbered starting from 1 and here v 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 n1 exactly once.

Input

The first line of the input contains a single integer t (1t104), the number of test cases.

The descriptions of the test cases follow.

The first line of each test case contains a single integer n (1n2105), the length of the permutation p.

The second line of each test case contains exactly n integers: p1,p2,,pn (0pin1), elements of permutation p.

It is guaranteed that the sum of n over all test cases does not exceed 2105.

Output

For each test case print the answer in a single line: the number of subsegments 1lrn of this permutation such that mex(pl,pl+1,,pr)>med(pl,pl+1,,pr).

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.

 

解题思路

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

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

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

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

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

  具体来说是这样的,对于mex=i的最小区间[l,r],如果p[i]>r,即i在区间的右边,这时我们应该枚举右端点求满足条件的左端点数目。如果如果p[i]<l,即i在区间的左边,这时我们应该枚举左端点求满足条件的右端点数目。这样我们就可以遍历的过程中不重复地枚举n个位置(因为枚举的位置下一次就变成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 @   onlyblues  阅读(88)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
Web Analytics
点击右上角即可分享
微信分享提示