【bzoj3224】【Tyvj 1728】 普通平衡树 树状数组
您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
1. 插入$x$数
2. 删除$x$数(若有多个相同的数,因只删除一个)
3. 查询$x$数的排名(若有多个相同的数,因输出最小的排名)
4. 查询排名为$x$的数
5. 求$x$的前驱(前驱定义为小于$x$,且最大的数)
6. 求$x$的后继(后继定义为大于$x$,且最小的数)
数据范围:操作数$≤10^5$,$x≤10^9$。
这一题用平衡树做的方法是显然的,然而平衡树太长太慢,我们考虑写一个优美一点的算法。
首先先离散化所有读入的数(别离散化操作4!!!!)
然后,插入和删除操作显然(直接在离散化后的x处,赋值+1或者-1即可)
查询某个数的排名也是显然的。
考虑查询排名为$x$如何高速完成,此处不妨设$x$为正整数。
我们求一个最小的$p$,使得$2^p≥cnt$,其中$cnt$为树状数组的长度。
设当前访问到的点为$id$(初始为$0$)
我们每次查询$a[id+2^p]$上的值是否小于$x$,如果是$x$,那么$id+=2^p$,然后$x-=a[id]$。
然后p--即可。
最后输出$id+1$即是第$k$大的数。
求前驱和后继:先求出给出的数是第几大的,然后$+1$或$-1$即可。
代码奇短
1 #include<bits/stdc++.h> 2 #define M 200000 3 #define lowbit(x) ((x)&(-x)) 4 using namespace std; 5 int a[M]={0},c[M]={0},n,cnt=0,m; 6 int op[M]={0},b[M]={0}; 7 void add(int x,int k){for(;x<=n;x+=lowbit(x)) a[x]+=k;} 8 int sum(int x){int k=0;for(;x;x-=lowbit(x)) k+=a[x]; return k;} 9 int getkth(int k){ 10 int id=0; 11 for(int i=n;i;i>>=1){ 12 if(k>a[id+i]) 13 k-=a[id+i],id+=i; 14 } 15 return id+1; 16 } 17 int main(){ 18 scanf("%d",&m); 19 for(int i=1;i<=m;i++){ 20 scanf("%d%d",op+i,b+i); 21 if(op[i]!=4) c[++cnt]=b[i]; 22 } 23 sort(c+1,c+cnt+1); 24 for(int i=1;i<=m;i++) 25 if(op[i]!=4) b[i]=lower_bound(c+1,c+cnt+1,b[i])-c; 26 for(n=1;n<=cnt;n<<=1); 27 for(int i=1;i<=m;i++){ 28 if(op[i]==1) add(b[i],1); 29 if(op[i]==2) add(b[i],-1); 30 if(op[i]==3) printf("%d\n",sum(b[i]-1)+1); 31 if(op[i]==4) printf("%d\n",c[getkth(b[i])]); 32 if(op[i]==5) printf("%d\n",c[getkth(sum(b[i]-1))]); 33 if(op[i]==6) printf("%d\n",c[getkth(sum(b[i])+1)]); 34 } 35 }