【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 }    
posted @ 2018-11-03 19:47  AlphaInf  阅读(303)  评论(2编辑  收藏  举报