Codeforces Round 915 (Div. 2) D. Cyclic MEX 模拟、单调队列

本文介绍了一种算法,用于在一个长度为n的排列中,通过循环移位来最小化mex值。使用单调队列存储mex值并动态更新,以压缩数量并减少处理项。C++代码展示了如何实现这一过程。
摘要由CSDN通过智能技术生成

题目链接

题目大意

        给定一个长度为 n (1<=n<=1e6) 的排列 a,a [ i ] ​∈[0,n−1]。你可以将这个排列进行循环移位,最小化 \sum_{i=1}^{n} mex_{j=1}^{i} a [ j ] ​ 的值。

        mex[b1,b2,b2……,bn]=不同于b1~bn的最小的非负整数。

思路

        首先,\sum_{i=1}^{n} 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;
}

  • 8
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值