给定排列p(0~n - 1), 定义花费为p的每个前缀的mex的和,排列p可以进行任意次循环左移,求最大花费

文章介绍了两种方法,利用线段树数据结构解决数组中0向左移动的问题,计算每次移动后mex值的和,其中法一使用了递归构建线段树,法二采用deque处理并更新mex值。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

法一:线段树

#include<bits/stdc++.h>
using namespace std;
#define lson p << 1
#define rson p << 1 | 1
#define int long long
#define pb push_back
const int maxn = 1e6 + 5, inf = 1e9 + 5;
int a[maxn], b[maxn];
int t[maxn << 2], f[maxn];
void push_up(int p){
	t[p] = min(t[lson], t[rson]);
}
void build(int p, int l, int r){
	if(l == r){
		t[p] =  inf;
		return;
	}
	int mid = (l + r) >> 1;
	build(lson, l, mid);
	build(rson, mid + 1, r);
	push_up(p);
}
int query(int p, int l, int r, int val){
	if(l == r){
		return l;
	}
	int mid = (l + r) >> 1;
	if(t[rson] < val) return query(rson, mid + 1, r, val);
	else return query(lson, l, mid, val);
}
void insert(int p, int l, int r, int pos, int val){
	if(l == r){
		t[p] = val;
		return;
	}
	int mid = (l + r) >> 1;
	if(pos <= mid) insert(lson, l, mid, pos, val);
	else insert(rson, mid + 1, r, pos, val);
	push_up(p);
}
void solve(){
	int n;
	cin >> n;
	int pos = -1;
	for(int i = 0; i < n; i++){
		cin >> a[i];
//		b[i] = a[i];
		if(a[i] == 0) pos = i;
	} 
	if(n == 1){
		cout << 1 << '\n';
		return;
	}
	int m = 0;
	pos = (pos + 1) % n;
	for(int i = 0; i < n - 1; i++){
		b[++m] = a[(pos + i) % n];
	}
	for(int i = 1; i < n; i++){
		a[i] = b[i];//a数组存从0开始往右的序列
	}
	n--;
	build(1, 1, n);
	f[1] = a[1];//一开始0在排列最右边,答案为0(忽略最右边的mex值,始终为n + 1)
				//f[i]表示0往左移动i次的序列mex值之和
	insert(1, 1, n, 1, a[1]);
	for(int i = 2; i <= n; i++){
		int pos = -1;
		if(t[1] > a[i]){
			f[i] = i * a[i];//位置i(即1~i)的mex值取决于a数组[i+1, n]的后缀最小值
		}
		else{
			pos = query(1, 1, n, a[i]);//找到从右往左第一个小于a[i]的位置,那么[pos+1, i]的mex都为a[i]
			f[i] = f[pos] + a[i] * (i - pos);
		}
		insert(1, 1, n, i, a[i]);
	}
	int res = 0;
	for(int i = 1; i <= n; i++){
		res = max(res, f[i] + n + 1);
	}
	cout << res << '\n';
}
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	int T;
	cin >> T;
	while(T--){
		solve();
	}
	return 0;
}

法二:

#include <bits/stdc++.h>

#define int long long

using namespace std;

int32_t main()
{
	cin.tie(nullptr)->sync_with_stdio(false);
	int q;
	cin >> q;
	while (q--)
	{
		int n;
		cin >> n;
		vector<int> a(n + 1);
		for (int i = 1; i <= n; ++i)
		{
			cin >> a[i];
		}
		deque<pair<int, int>> dq;
		vector<int> f(n + 1);
		int mex = 0;
		int sum = 0;
		for (int i = 1; i <= n; ++i)
		{
			f[a[i]]++;
			while (f[mex])
			{
				mex++;
			}
			dq.push_back({mex, 1});
			sum += mex;
		}
		int ans = sum;
		for (int i = 1; i < n; ++i)
		{
			pair<int, int> me = {a[i], 0};
			sum -= dq.front().first;
			dq.front().second--;
			if (dq.front().second == 0)
			{
				dq.pop_front();
			}
			while (!dq.empty() && dq.back().first >= a[i])
			{
				sum -= dq.back().first * dq.back().second;
				me.second += dq.back().second;
				dq.pop_back();
			}
			dq.push_back(me);
			sum = sum + me.first * me.second;
			dq.push_back({n, 1});
			sum += n;
			ans = max(ans, sum);
		}
		cout << ans << '\n';
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

__night_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值