D. Cyclic MEX
D. Cyclic MEX
For an array , define its cost as .
You are given a permutation of the set . Find the maximum cost across all cyclic shifts of .
is the smallest non-negative integer such that does not occur among .
A permutation of the set is an array consisting of distinct integers from to in arbitrary order. For example, is a permutation, but is not a permutation ( appears twice in the array), and is also not a permutation ( but there is in the array).
Input
Each test consists of multiple test cases. The first line contains a single integer () — the number of test cases. The description of the test cases follows.
The first line of each test case contains a single integer () — the length of the permutation .
The second line of each test case contain distinct integers () — the elements of the permutation .
It is guaranteed that sum of over all test cases does not exceed .
Output
For each test case, output a single integer — the maximum cost across all cyclic shifts of .
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 with cost .
In the second test case, the cyclic shift that yields the maximum cost is with cost .
解题思路
定义 表示前缀 的 值,容易知道 是单调递增的。现在考虑循环左移一次,即把原序列的 移到最后,看看每个原本的 会有什么变化。
首先原本的 会被删除。再考虑 ,如果 ,那么不会改变;如果 ,那么就会变成 。将变化后的 左移一个单位,并在最后添加 ,那么就会得到 序列循环左移一个单位后的前缀 值。
下表是以 按照上述过程模拟的表格:
代码实现只需用依次枚举 然后用 std::deque
去模拟这个过程,对每种情况取 的最大值即可。不过如果每次都从队尾开始枚举,把大于 的值都改成 ,那么整个模拟的时间复杂度就会达到 。
改进的方法是用队列存储每个值以及对应的个数,由于 递增因此相同的值必然是连续的一段。每次从队尾开始枚举时,只需统计比 大的值的总数 ,并将这些值从队列中删除,最后再把 压入队尾,同时还要把 压入队尾。另外还要用一个变量来维护队列中的 的总和。
由于在模拟的过程中一共往队列中插入 个元素,因此删除操作执行的次数也是 级别的。
AC 代码如下,时间复杂度为 :
#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
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/17912095.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
2022-12-18 Add Edges to Make Degrees of All Nodes Even