题目大意
给定一个长度为 n (1<=n<=1e6) 的排列 a,a [ i ] ∈[0,n−1]。你可以将这个排列进行循环移位,最小化 的值。
mex[b1,b2,b2……,bn]=不同于b1~bn的最小的非负整数。
思路
首先, mex[a1,a2,...,an]的值是单调不减的,比如a1的mex值为p(即p为最小的不等于a1的非负整数),而下一个数a2的mex值即为不等于a1,a2的最小的非负整数,若a2==p,则其mex值将增大,若a2!=p,则其mex值仍未p。
故而,用一个单调队列存入所有位置的mex值,选择向左循环位移,每次取出队首,再从对尾向前扫,若有mex值大于移除的队首的值则更改其为所移除队首的值,最后再向对位加入mex值n(对尾的mex只会是n)。这个过程中可以压缩每个值的数量,以便方便修改,这样压缩要处理的项也会逐渐减少。
code
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int, int>;
const int N = 1e6 + 10;
int p[N];
bool vis[N];
void solve() {
int n;
cin >> n;
for (int i = 1; i <= n; ++i) {
cin >> p[i];
vis[p[i]] = false;
}
vis[n] = 0;//要注意初始话点n因为序列的最大mex为n,要用到
int now = 0;
int ans = 0;
map<int, int>mp;
deque<pair<int, int>>q;//单调队列
int tmp = -1;
for (int i = 1; i <= n; ++i) {
vis[p[i]] = 1;
while (vis[now]) {
now++;
}
if (tmp == -1) {
mp[now]++;
tmp = now;
//当n==1时直接放入
if (n == 1) {
ans+=tmp * mp[tmp];
q.push_back({ tmp,mp[tmp] });
}
}
else if (i == n) {
if (now == tmp) {
mp[tmp]++;
ans += tmp * mp[tmp];
q.push_back({ tmp,mp[tmp] });
}
else {
ans += tmp * mp[tmp];
q.push_back({ tmp,mp[tmp] });
mp[now]++;
ans += now * mp[now];
q.push_back({ now,mp[now] });
}
}
else if (now != tmp) {
ans += tmp * mp[tmp];
q.push_back({ tmp,mp[tmp] });
tmp = now;
mp[tmp]++;
}
else mp[tmp]++;
}
//while (q.size()) {
// pii tp = q.front();
// cout << tp.first << " " << tp.second << '\n';
// q.pop_front();
//}
int res = ans;
for (int i = 1; i < n; ++i) {
if (!q.size()) break;
pii tp = { p[i],0 };
res -= q.front().first;
q.front().second--;
if (!q.front().second) q.pop_front();
while (q.size() && q.back().first >= p[i]) {
res -= q.back().first * q.back().second;
tp.second += q.back().second;
q.pop_back();
}
q.push_back(tp);
res += n + tp.first * tp.second;
q.push_back({ n,1 });
ans = max(ans, res);
}
cout << ans << '\n';
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while (t--) solve();
return 0;
}