题解:P13091 [FJCPC 2025] 中位数

解题思路

题意很好理解,每次可以把三个数字替换为一个中位数,最后使得剩下的数最大。

我们会发现,这道题目的答案符合单调性,序列中元素越大,结果就越大(因为总不可能3个数字中一个数变大导致其中位数变小),所以我们可以用二分答案实现。

我们换个思路想想,转化题意,对于每个\(mid\),我们看能否使得最后的结果大于等于他,那么我们发现,比\(mid\)小的数字,实际上没什么区别,我们只需要区分大于等于\(mid\)以及小于\(mid\)的数即可,我们可以用一个简单的离散化思路,把所有不小于\(mid\)的数字变成1,其余变成0。这样我们的目标就是让最后的结果为1即可。

我们试图寻找规律,枚举3个数字不同的组合。其中我们枚举的是组合并非排列,因为不管怎么排列3个数始终是选择中位数的。那我们开始:

  • 000:得0,即消除2个0
  • 001:得0,即消除0、1各一个
  • 011:得1,即消除0、1各一个
  • 111:得1,即消除2个1

我们考虑到,由于最后要找1,我们就尽可能把1保留,把0消除,我们发现,上述情况中,第一个只消除0,肯定赚到了,中间两种不赚不亏,最后一种绝对亏本,不能选。

我们制定策略:

  • 对于原有的连续的0,我们可以将其贪心区间操作,使其最终长度为1或2。
  • 对于原有的连续的1,我们肯定选择最后再消除。
  • 对于原有连续2个0以及1个1的情况,无论怎么消除,我们都只能变成0,不过此时可以与后续的0继续拼接。
  • 对于原有连续2个1以及1个0的情况,我们可以留到最后再消除。

处理完整的一个01序列后,最后剩余1的数量比0多则存在某种解,否则就无解。

我们用栈模拟,直接记录连续个数,只要能确保最大化消掉0即可。

废话不多说,直接上代码

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5 + 10;
int n, a[N], b[N], s[N];
inline bool check(int mid) {
	int cnt = 0;
	for (int i = 1 ; i <= n ; i++) {
		if (a[i] >= mid) b[i] = 1;
		else b[i] = 0;
	}
	for (int i = 1 ; i <= n ; i++) {
		if (cnt > 1 && s[cnt] == 0 && s[cnt - 1] == 0) cnt--;
		else s[++cnt] = b[i];
	}
	int ans = 0;
	for (int i = 1 ; i <= cnt ; i++) {
		if (s[i] == 1) ans++;
		else ans--;
	}
	return ans > 0;
}
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr), cout.tie(nullptr);
	int t;
	cin >> t;
	while (t--) {
		cin >> n;
		for (int i = 1 ; i <= n ; i++) {
			cin >> a[i];
		}
		int l = 1, r = 1e9;
		while (l < r) {
			int mid = (l + r + 1) >> 1;
			if (check(mid)) l = mid;
			else r = mid - 1;
		}
		cout << l << "\n";
	}
	return 0;
}
posted @ 2025-08-27 15:25  fengjunxiao2014  阅读(19)  评论(0)    收藏  举报