1.题目大意:给出一个序列和 q q q次操作,每次操作要么序列中添加 k ( 1 ≤ k ≤ n ) k(1 \leq k \leq n) k(1≤k≤n),要么就找到数组中的第 k k k小的元素删除,最后如果序列存在元素则输出任意一个,否则输出 0 0 0
2.一看到第 k k k小,想到了快排和归并,但是二者的时间复杂度都是 O ( n ) O(n) O(n)的,对于 q q q次询问,时间复杂度达到了 O ( n 2 ) O(n^2) O(n2),肯定不行的。看数据,应该需要我们在 l o g n logn logn的时间内添加和删除元素
3.最重要的一点,因为 k k k的取值范围不超过 n n n,这就好处理多了,我们考虑树状数组,然后对每个数出现的次数建树,这样就能 l o g n logn logn地插入数字,并且统计次数。但是最后删除第 k k k小,需要从 1 1 1开始遍历直到前缀和等于 k k k吗?显然不需要,树状数组维护的前缀和是递增的,因此我们直接使用二分查找即可 l o g n logn logn时间内解决
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <math.h>
#include <cstdio>
#include <string>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <unordered_map>
using namespace std;
#define lowbit(x) (x&(-x))
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> P;
const double eps=1e-8;
const double pi=acos(-1.0);
const int inf=0x3f3f3f3f;
const ll INF=1e18;
const int Mod=1e9+7;
const int maxn=1e6+10;
int t[maxn];
int n,q,k,x;
void update(int i,int k){
while(i<=maxn){
t[i]+=k;
i+=lowbit(i);
}
}
ll ask(int i){
ll ans=0;
for(;i;i-=lowbit(i)){
ans+=t[i];
}
return ans;
}
int getAns(){
for(int i=1;i<=n;i++)
if(t[i]) return i;
return 0;
}
int main(){
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
scanf("%d%d",&n,&q);
for(int i=0;i<n;i++){
scanf("%d",&x);
update(x,1);
}
while(q--){
scanf("%d",&k);
if(k>0){
update(k,1);
}else{
k=-k;
int l=1,r=n+10;
while(l<r){
int mid=(l+r)/2;
if(ask(mid)<k) l=mid+1;
else r=mid;
}
update(l,-1);
}
}
printf("%d\n",getAns());
return 0;
}