D. Cyclic MEX

D. Cyclic MEX

For an array a, define its cost as i=1nmex([a1,a2,,ai]).

You are given a permutation p of the set {0,1,2,,n1}. Find the maximum cost across all cyclic shifts of p.

mex([b1,b2,,bm]) is the smallest non-negative integer x such that x does not occur among b1,b2,,bm.

A permutation of the set {0,1,2,...,n1} is an array consisting of n distinct integers from 0 to n1 in arbitrary order. For example, [1,2,0,4,3] is a permutation, but [0,1,1] is not a permutation (1 appears twice in the array), and [0,2,3] is also not a permutation (n=3 but there is 3 in the array).

Input

Each test consists of multiple test cases. The first line contains a single integer t (1t105) — the number of test cases. The description of the test cases follows.

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

The second line of each test case contain n distinct integers p1,p2,,pn (0pi<n) — the elements of the permutation p.

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

Output

For each test case, output a single integer — the maximum cost across all cyclic shifts of p.

Example

input

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

output

15
5
31
1

Note

In the first test case, the cyclic shift that yields the maximum cost is [2,1,0,5,4,3] with cost 0+0+3+3+3+6=15.

In the second test case, the cyclic shift that yields the maximum cost is [0,2,1] with cost 1+1+3=5.

 

解题思路

  定义 fi 表示前缀 [1,i]mex 值,容易知道 fi 是单调递增的。现在考虑循环左移一次,即把原序列的 p1 移到最后,看看每个原本的 fi 会有什么变化。

  首先原本的 f1 会被删除。再考虑 f2fn,如果 fi<p1,那么不会改变;如果 fi>p1,那么就会变成 p1。将变化后的 fi 左移一个单位,并在最后添加 n,那么就会得到 p 序列循环左移一个单位后的前缀 mex 值。

  下表是以 p=[3102] 按照上述过程模拟的表格:

ppff3102102300240234102302310234111402312310111400042310310200040024

  代码实现只需用依次枚举 pi 然后用 std::deque 去模拟这个过程,对每种情况取 i=1nfi 的最大值即可。不过如果每次都从队尾开始枚举,把大于 pi 的值都改成 pi,那么整个模拟的时间复杂度就会达到 O(n2)

  改进的方法是用队列存储每个值以及对应的个数,由于 fi 递增因此相同的值必然是连续的一段。每次从队尾开始枚举时,只需统计比 pi 大的值的总数 c,并将这些值从队列中删除,最后再把 (pi,c) 压入队尾,同时还要把 (n,1) 压入队尾。另外还要用一个变量来维护队列中的 fi 的总和。

  由于在模拟的过程中一共往队列中插入 O(n) 个元素,因此删除操作执行的次数也是 O(n) 级别的。

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

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

typedef long long LL;
typedef pair<int, int> PII;

const int N = 1e6 + 10;

int a[N];
bool vis[N];

void solve() {
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d", a + i);
    }
    memset(vis, 0, n + 10);
    LL s = 0;
    deque<PII> q;
    for (int i = 0, j = 0; i < n; i++) {
        vis[a[i]] = true;
        while (vis[j]) {
            j++;
        }
        q.push_back({j, 1});
        s += j;
    }
    LL ret = 0;
    for (int i = 0; i < n; i++) {
        s -= q.front().first;    // 把第一个元素删除
        if (--q.front().second == 0) q.pop_front();    // 这个值没有了
        int cnt = 0;    // 统计比a[i]大的数的个数
        while (!q.empty() && q.back().first > a[i]) {
            s -= 1ll * q.back().first * q.back().second;    // 将这些数从队列中删掉
            cnt += q.back().second;
            q.pop_back();
        }
        s += 1ll * a[i] * cnt + n;    // 这些数全部变成a[i]
        q.push_back({a[i], cnt});    // 并把a[i]及其个数插到队尾
        q.push_back({n, 1});    // 最后n插到队尾
        ret = max(ret, s);
    }
    printf("%lld\n", ret);
}

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

 

参考资料

  Codeforces Round 915 (Div. 2) Editorial:https://codeforces.com/blog/entry/123384

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