题目
思路
1. 若序列合法
2. 那么最后一次操作一定用到最大值和最小值
- 去掉最大值和最小值, 继续操作1
每次都保证一定会用到该值,所以保证解是正确的, 当时做的时候一直想不开,看了大佬的题解幡然醒悟。
现在问题转化为如何更快的检查序列是否合法?显然暴力的话\(O(n^2)\)肯定会超时。
能不能二分找答案?
因为答案所在的区间一定是\([0, n/2]\), 最坏的情况下将每一个数对都排以下。假设答案是操作\(x\)次,则区间\([x, n - x]\) (这里表示从1~n枚举)必然合法,并且若操作\(x + 1\)次,则\([x + 1, n - x - 1]\)为操作\(x\)的子区间也合法,只有小于\(x\)才不合法,所以具有二段性。
复杂度\(O(nlogn)\)
参考:清风qwq
Code
#include <bits/stdc++.h>
using i64 = long long;
int n;
std::vector<int> a;
// 检查 1~n中属于区间[l,r]的数时候排序合法
bool check(int l, int r) {
int st = l;
for(int i = 1; i <= n; i ++) {
if(a[i] >= l && a[i] <= r) {
if(a[i] != st) return false;
st ++;
}
}
return true;
}
void solve() {
std::cin >> n;
a.resize(n + 1, 0);
for(int i = 1; i <= n; i ++) {
std::cin >> a[i];
}
int l = 0, r = n / 2;
while(l < r) {
int mid = l + r >> 1;
if(check(mid + 1, n - mid)) r = mid;
else l = mid + 1;
}
std::cout << l << "\n";
}
int main() {
int _;
std::cin >> _;
while(_ --) {
solve();
}
}