题目描述
要你维护一个可重集,每次操作插入一个数 (k), 或者删除第 (k) 大元素。
询问最后是否还有元素,如果有则任意输出一个元素。
正解
虽然这题直接上线段树就可以过这题,但是其实还有时空更加优秀的做法。
二分答案 (x),判断是否最后剩下一个小于等于 (x) 的元素。
把元素看成两类数,一类小于等于 (x),另一类大于 (x)。
这样只需要记录两个桶 (l_{siz}) 和 (r_{siz}) 就可以 (O(1)) 进行插入或者删除操作了。
时间复杂度 (O(n log V)),空间复杂度 (O(n))。
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
int n, q;
int a[N], b[N];
inline int read() {
int x = 0; bool neg = false; char ch = getchar();
while(!isdigit(ch)) (ch == '-') && (neg = true), ch = getchar();
while(isdigit(ch)) x = x * 10 + ch - '0', ch = getchar();
return neg ? -x : x;
}
bool check(int x) {
int lsiz = 0, rsiz = 0;
for(int i = 1; i <= n; ++i)
if(a[i] <= x) {
++lsiz;
} else {
++rsiz;
}
for(int i = 1; i <= q; ++i) {
if(b[i] < 0) {
if(-b[i] <= lsiz) --lsiz;
else --rsiz;
} else {
if(b[i] <= x) ++lsiz;
else ++rsiz;
}
}
return lsiz > 0;
}
int main() {
n = read(), q = read();
int rem = n;
for(int i = 1; i <= n; ++i)
a[i] = read();
for(int i = 1; i <= q; ++i) {
b[i] = read();
if(b[i] < 0) --rem;
else ++rem;
}
if(rem <= 0) {
puts("0");
return 0;
}
int l = 1, r = n, mid, mp = n;
while(l <= r) {
mid = (l + r) >> 1;
if(check(mid)) {
if(mp > mid) mp = mid;
r = mid - 1;
} else {
l = mid + 1;
}
}
printf("%d
", mp);
return 0;
}