替罪羊树 普通平衡树
Description
您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
1. 插入x数
2. 删除x数(若有多个相同的数,因只删除一个)
3. 查询x数的排名(若有多个相同的数,因输出最小的排名)
4. 查询排名为x的数
5. 求x的前驱(前驱定义为小于x,且最大的数)
6. 求x的后继(后继定义为大于x,且最小的数)
Input
第一行为n,表示操作的个数,下面n行每行有两个数opt和x,opt表示操作的序号(1<=opt<=6)
Output
对于操作3,4,5,6每行输出一个数,表示对应答案
Sample Input
10
1 106465
4 1
1 317721
1 460929
1 644985
1 84185
1 89851
6 81968
1 492737
5 493598
1 106465
4 1
1 317721
1 460929
1 644985
1 84185
1 89851
6 81968
1 492737
5 493598
Sample Output
106465
84185
492737
84185
492737
HINT
1.n的数据范围:n<=100000
2.每个数的数据范围:[-2e9,2e9]
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define lc ch[x][0] 4 #define rc ch[x][1] 5 #define ff fa[x] 6 int const N=1000000+10; 7 int ch[N][2],v[N],tot[N],sz[N],trsz[N],whsz[N],fa[N],tf[N],sv[N],st[N]; 8 int len,n,root,ck[N],t,tt; 9 double alp=0.75; 10 multiset<int> s; 11 int kk(){ 12 if(t) return ck[t--]; 13 else return ++len; 14 } 15 int find(int val,int x){ 16 if(val<v[x] && lc) return find(val,lc); 17 if(val>v[x] && rc) return find(val,rc); 18 return x; 19 } 20 void build(int val,int x,int f){ 21 lc=rc=tf[x]=0; 22 ff=f; 23 v[x]=val; 24 tot[x]=sz[x]=trsz[x]=whsz[x]=1; 25 } 26 void update(int x,int y,int z,int k){ 27 if(!x) return; 28 trsz[x]+=y; 29 sz[x]+=z; 30 whsz[x]+=k; 31 update(ff,y,z,k); 32 } 33 void dfs(int x){ 34 if(!x) return; 35 dfs(lc); 36 if(!tf[x]) sv[++tt]=v[x],st[tt]=tot[x]; 37 ck[++t]=x; 38 dfs(rc); 39 } 40 int readd(int l,int r,int f){ 41 if(l>r) return 0; 42 int mid=(l+r)/2; 43 int x=kk(); 44 ff=f; 45 v[x]=sv[mid]; 46 tot[x]=st[mid]; 47 lc=readd(l,mid-1,x); 48 rc=readd(mid+1,r,x); 49 whsz[x]=st[mid]+whsz[lc]+whsz[rc]; 50 sz[x]=trsz[x]=r-l+1; 51 tf[x]=0; 52 return x; 53 } 54 void rebuild(int x){ 55 tt=0; 56 dfs(x); 57 if(x==root) root=readd(1,tt,0); 58 else { 59 update(ff,0,-(sz[x]-trsz[x]),0); 60 if(ch[ff][0]==x) ch[ff][0]=readd(1,tt,ff); 61 else ch[ff][1]=readd(1,tt,ff); 62 } 63 } 64 void find_rebuild(int x,int val){ 65 if(sz[lc]>sz[x]*alp || sz[rc]>sz[x]*alp || sz[x]-trsz[x]>sz[x]*0.3) rebuild(x); 66 else if(val!=v[x]) find_rebuild(val<v[x]? lc: rc,val); 67 } 68 69 void add(int val){ 70 if(!root){ 71 build(val,root=kk(),0); 72 return ; 73 } 74 int x=find(val,root); 75 if(val==v[x]){ 76 tot[x]++; 77 if(tf[x]) tf[x]=0,update(x,1,0,1); 78 else update(x,0,0,1); 79 } 80 else if(val<v[x]) build(val,lc=kk(),x),update(x,1,1,1); 81 else build(val,rc=kk(),x),update(x,1,1,1); 82 find_rebuild(root,val); 83 } 84 void del(int val){ 85 int x=find(val,root); 86 if(v[x]!=val) return; 87 tot[x]--; 88 if(!tot[x]) tf[x]=1,update(x,-1,0,-1); 89 else update(x,0,0,-1); 90 find_rebuild(x,val); 91 } 92 int rk(int val){ 93 int ret=0,x=root; 94 while (x){ 95 if(v[x]>val) x=lc; 96 else if(v[x]==val){ 97 ret+=whsz[lc]+1; return ret; 98 } 99 else ret+=whsz[lc]+tot[x],x=rc; 100 } 101 } 102 int nth(int k){ 103 int x=root; int num=0; 104 while (1){ 105 if(whsz[lc]>=k) x=lc; 106 else if(k>whsz[lc]+tot[x]) k-=whsz[lc]+tot[x],x=rc; 107 else return v[x]; 108 } 109 } 110 int pre(int k){ 111 return *(--s.lower_bound(k)); 112 } 113 int nxt(int k){ 114 return *(s.upper_bound(k)); 115 } 116 int main(){ 117 scanf("%d",&n); 118 while (n--){ 119 int id,x; 120 scanf("%d%d",&id,&x); 121 if(id==1) add(x),s.insert(x); 122 if(id==2) del(x),s.erase(s.find(x)); 123 if(id==3) printf("%d\n",rk(x)); 124 if(id==4) printf("%d\n",nth(x)); 125 if(id==5) printf("%d\n",pre(x)); 126 if(id==6) printf("%d\n",nxt(x)); 127 } 128 return 0; 129 }
替罪羊树的核心思想: 暴力重构,不平衡就重构,总之就是一眼不合就重构。
这里的这种标记每个被删除的写法,非常不适合求前趋和后继,还是建议删除的时候,真的删除就可以了,因为如果不平衡了以后可以重构。
前趋和后继写的我心累,直接给了一个multiset