题目描述
您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
- 插入 x x x 数
- 删除 x x x 数(若有多个相同的数,因只删除一个)
- 查询 x x x 数的排名(排名定义为比当前数小的数的个数 + 1 +1 +1 )
- 查询排名为 x x x 的数
- 求 x x x 的前驱(前驱定义为小于 x x x,且最大的数)
- 求
x
x
x 的后继(后继定义为大于
x
x
x,且最小的数)
输入格式
第一行为 n n n,表示操作的个数,下面 n n n 行每行有两个数 o p t opt opt 和 x x x, o p t opt opt 表示操作的序号 1 ≤ o p t ≤ 6 1≤opt≤6 1≤opt≤6 )
输出格式
对于操作 3 , 4 , 5 , 6 3,4,5,6 3,4,5,6 每行输出一个数,表示对应答案
题解:
这道题我用的权值线段树,主要是因为还不会平衡树
我们先把除了操作4的所有数离散化处理一下
对于1,2两个操作,直接在权值线段树上+1,-1即可
对于操作3:查询x数的排名,我们可以查询[1,x-1]区间中数字出现的个数+1即可
对于操作4:查询第k小的数,利用权值线段树的性质,根据出现数的个数选择递归左儿子还是右儿子即可
对于操作5:求x的前驱,求出[1,x-1]区间内的数字出现次数,然后这个区间内最靠近x的数就是x的前驱
对于操作6:求x的后驱,求出[1,x]区间内的数字出现次数,然后这个区间后面的第一个数就是x的后驱
AC代码:
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e5+50;
struct node{ int l,r,sum; }t[MAXN<<2];
int a[MAXN],op[MAXN]; vector<int> v;
inline void build(int rt,int l,int r){
t[rt].l=l,t[rt].r=r,t[rt].sum=0;
if(l==r) return;
int mid=(l+r)>>1;
build(rt<<1,l,mid); build(rt<<1|1,mid+1,r);
}
inline void update(int rt,int k,int w){//操作1,2
if(t[rt].l==t[rt].r) { t[rt].sum+=w; return; }
int mid=(t[rt].l+t[rt].r)>>1;
if(k<=mid) update(rt<<1,k,w);
else update(rt<<1|1,k,w);
t[rt].sum = t[rt<<1].sum+t[rt<<1|1].sum;
}
inline int query(int rt,int ql,int qr){//查询ql到qr区间中数字的出现次数
if(ql>qr) return 0;
if(ql<=t[rt].l && t[rt].r<=qr) return t[rt].sum;
int mid=(t[rt].l+t[rt].r)>>1;
if(qr<=mid) return query(rt<<1,ql,qr);
else if(ql>mid) return query(rt<<1|1,ql,qr);
else return query(rt<<1,ql,mid)+query(rt<<1|1,mid+1,qr);
}
inline int kth(int rt,int k){//区间第k小
if(t[rt].l==t[rt].r) return t[rt].l;
int mid=(t[rt].l+t[rt].r)>>1;
if(t[rt<<1].sum>=k) return kth(rt<<1,k);
else return kth(rt<<1|1,k-t[rt<<1].sum);
}
inline int Id(int x){ return lower_bound(v.begin(),v.end(),x)-v.begin()+1; }
int main(){
//freopen("C:\\Users\\Administrator\\Desktop\\in.txt","r",stdin);
int n; scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d%d",&op[i],&a[i]);
if(op[i]==4) continue;
v.push_back(a[i]);
}
sort(v.begin(),v.end());
v.erase(unique(v.begin(),v.end()),v.end());
int m=v.size();
build(1,1,m);
for(int i=1;i<=n;i++){
if(op[i]==1) update(1,Id(a[i]),1);
else if(op[i]==2) update(1,Id(a[i]),-1);
else if(op[i]==3) printf("%d\n",query(1,1,Id(a[i])-1)+1);
else if(op[i]==4) printf("%d\n",v[kth(1,a[i])-1]);
else if(op[i]==5) printf("%d\n",v[kth(1,query(1,1,Id(a[i])-1))-1]);
else if(op[i]==6) printf("%d\n",v[kth(1,query(1,1,Id(a[i]))+1)-1]);
}
return 0;
}