题解: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;
}

浙公网安备 33010602011771号