T701316 重排 (rearrange) 赛后题解

题目传送门 + 原题传送门

思路

静态原问题

给定长度为 \(n\) 的正整数序列 \(a\),你可以任意排列序列 \(a\),使得以下式子的值最大:

\[f(a)=\sum_{i=2}^{n} |a_{i}-a_{i-1}| \]

发现有绝对值求最大值,考虑将数分为大、小两部分,交错放置。
将大的中最小的、小的中最大的放两边更优。

令大的和为 sum2,小的和为 sum1,大的中最小值为 bmin,小的中最大值为 smax,若 n 为奇数令中位数为 mid
\(\begin{align*}\begin{split}ans= \left \{\begin{array}{ll} 2(sum2-sum1)-(bmin-smax), & n \equiv 0\pmod 2\\ 2(sum2-sum1)-\min(bmin-mid,mid-smax), &n \equiv 1\pmod 2\end{array}\right.\end{split}\end{align*}\)

动态问题

需要动态维护以上 4 或 5 个量,求出答案即可。

做法

对顶堆维护,参考 P1168 中位数

时间复杂度:\(O(m\log_2m)\),可以通过 \(m\le 3\times 10^6\)

代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
int a[3000005];
signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	freopen("rearrange.in", "r", stdin);
	freopen("rearrange.out", "w", stdout);
	int n, T;
	cin >> n >> T;
	for(int i = 1; i <= n; i ++)
	{
		cin >> a[i];
	}
	priority_queue<int> pq1; //小的部分,大根堆,top 为 smax
	priority_queue<int, vector<int>, greater<int> > pq2; //大的部分,小根堆,top 为 bmin
	int ans = 0, las = 0, sum1 = 0, sum2 = 0; //las 为上一轮答案(此题强制在线)
	if(n == 1) //特判 n=1,因为对顶堆中至少得有 2 个元素
	{
		cout << 0;
		return 0;
	}
	pq1.push(min(a[1], a[2])), pq2.push(max(a[1], a[2]));
	sum1 = min(a[1], a[2]), sum2 = max(a[1], a[2]);
	las = max(a[1], a[2]) - min(a[1], a[2]);
	ans ^= las;
	int mid;
	for(int i = 3; i <= n; i ++)
	{
		a[i] ^= las * T;
		if(i & 1)
		{
			if(a[i] < pq1.top()) //属于小的一半
			{
				mid = pq1.top();
				sum1 -= pq1.top() - a[i];
				pq1.pop();
				pq1.push(a[i]);
			}
			else if(a[i] > pq2.top()) //属于大的一半
			{
				mid = pq2.top();
				sum2 -= pq2.top() - a[i];
				pq2.pop();
				pq2.push(a[i]);
			}
			else //为中位数
			{
				mid = a[i];
			}
			las = 2 * (sum2 - sum1) - min(pq2.top() - mid, mid - pq1.top());
			ans ^= las;
		}
		else
		{
			if(mid <= a[i]) //把中位数放回去
			{
				sum1 += mid;
				pq1.push(mid);
			}
			else
			{
				sum2 += mid;
				pq2.push(mid);
			}
			if(a[i] < pq1.top()) //属于小的一半
			{
				if(pq1.size() == (i >> 1)) //堆满了
				{
					pq2.push(pq1.top());
					sum2 += pq1.top();
					sum1 -= pq1.top() - a[i];
					pq1.pop();
					pq1.push(a[i]);
				}
				else
				{
					sum1 += a[i];
					pq1.push(a[i]);
				}
			}
			else if(a[i] > pq2.top()) //属于大的一半
			{
				if(pq2.size() == (i >> 1)) //堆满了
				{
					pq1.push(pq2.top());
					sum1 += pq2.top();
					sum2 -= pq2.top() - a[i];
					pq2.pop();
					pq2.push(a[i]);
				}
				else
				{
					sum2 += a[i];
					pq2.push(a[i]);
				}
			}
			else
			{
				if(pq1.size() == (i >> 1)) //哪个没满放哪个
				{
					sum2 += a[i];
					pq2.push(a[i]);
				}
				else
				{
					sum1 += a[i];
					pq1.push(a[i]);
				}
			}
			las = 2 * (sum2 - sum1) - (pq2.top() - pq1.top());
			ans ^= las;
		}
	}
	cout << ans;
	return 0;
}

posted @ 2025-11-25 16:25  yuzihang  阅读(5)  评论(0)    收藏  举报