Tyvj 1728 普通平衡树

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 13242  Solved: 5675
[Submit][Status][Discuss]

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

Sample Output

106465
84185
492737

HINT

1.n的数据范围:n<=100000

2.每个数的数据范围:[-2e9,2e9]

Source

平衡树

思路

splay

事实上,哨兵还是挺有用的,减轻逻辑负担;

由于是模板题:

t节点数值,f节点父亲编号,sz以节点为根的子树尺寸,am节点中数的个数,s节点的左右子编号;

rot()单旋;

splay()伸展(这里没有用双旋,因为脑模单旋和双旋操作量和结果一样,实测双旋也并不比单旋快);

//我可能跟我师傅学了假的双旋,对于单侧链的操作和单旋一致,故而没有任何优化效果,所以和这位大佬学吧,浅谈splay的双旋

//好吧,可能是题目,实测双旋有很强的优化效果;

ins()添加数;

find1()把要查找的节点旋到树根;

find2()查找序号对应节点;

find3()查找前驱;

find4()查找后继;

del()删除数;

del():减少root节点的am值或是删除root节点;

case1:减少root节点的sz和am值并退出;

case2:

将root的右子树接到root的左子树中的最右子上,同时更新从root的左子树到左子树树中最右子的路径上的节点的sz值;

调整嫁接处的值,root改为原root的左子;

将原root的左子树的最右子旋到根的位置;

代码实现

 1 #include<cstdio>
 2 const int maxn=3e5;
 3 int n,ope,val;
 4 int rt,hd;
 5 int t[maxn],f[maxn],sz[maxn],am[maxn],s[maxn][2];
 6 void rot(int x){
 7     int y=f[x],z=f[y],l,r;
 8     l=s[y][0]==x?0:1,r=l^1;
 9     if(y==rt) rt=x;
10     else{
11         if(s[z][0]==y) s[z][0]=x;
12         else s[z][1]=x;
13     }
14     f[x]=z,f[y]=x,f[s[x][r]]=y;
15     s[y][l]=s[x][r],s[x][r]=y;
16     sz[y]=sz[s[y][0]]+sz[s[y][1]]+am[y];
17     sz[x]=sz[s[x][0]]+sz[s[x][1]]+am[x];
18 }
19 void splay(int x){while(x!=rt) rot(x);}
20 void ins(int k,int x,int fa){
21     if(!rt){rt=++hd,t[rt]=x,sz[rt]=1,am[rt]++;return;}
22     while(k) fa=k,++sz[k],k=s[k][x>t[k]];
23     k=s[fa][x>t[fa]]=++hd;
24     t[k]=x,sz[k]=1,f[k]=fa,am[k]++;
25     splay(k);
26 }
27 void find1(int k,int x){
28     if(!k) return;
29     while(s[k][x>t[k]]&&t[k]!=x) k=s[k][x>t[k]];
30     splay(k);
31 }
32 int find2(int k,int x){
33     if(x<=sz[s[k][0]]) return find2(s[k][0],x);
34     if(x==sz[s[k][0]]+1) return t[k];
35     return find2(s[k][1],x-sz[s[k][0]]-1);
36 }
37 int find3(int k,int x,int sum){
38     if(!k) return sum;
39     if(x>t[k]) return find3(s[k][1],x,t[k]);
40     else return find3(s[k][0],x,sum);
41 }
42 int find4(int k,int x,int sum){
43     if(!k) return sum;
44     if(x<t[k]) return find4(s[k][0],x,t[k]);
45     else return find4(s[k][1],x,sum);
46 }
47 void del(int k,int x){
48     if(am[k]>1){am[k]--,sz[k]--;return;}
49     x=s[k][1];
50     while(s[x][0]) x=s[x][0],sz[x]+=sz[s[k][0]];
51     f[s[k][0]]=x,s[x][0]=s[k][0],rt=s[k][1];
52     f[rt]=0;
53     splay(x);
54 }
55 int main(){
56     freopen("phs.in","r",stdin);
57     freopen("phs.out","w",stdout);
58     ins(rt,-2e9-10,0),ins(rt,2e9+10,0);
59     scanf("%d",&n);
60     for(int i=1;i<=n;i++){
61         scanf("%d%d",&ope,&val);
62         if(ope==1) ins(rt,val,0);
63         if(ope==2) find1(rt,val),del(rt,0);
64         if(ope==3) find1(rt,val),printf("%d\n",sz[s[rt][0]]);
65         if(ope==4) printf("%d\n",find2(rt,val+1));
66         if(ope==5) printf("%d\n",find3(rt,val,0));
67         if(ope==6) printf("%d\n",find4(rt,val,0));
68     }
69     return 0;
70 }
1.0

 又重新写了一遍,先手模了几棵二叉查找树,直观地认识了一下双旋对树结构的影响(对于“之”字形图,比较容易看出双旋的优越性);

然后,优化了一下逻辑,给出了正确的双旋,使代码更加美观,跑的也稍快了。

 1 #include<cstdio>
 2 const int maxn=1e5+10;
 3 int rt,ts;
 4 int t[maxn],sz[maxn],num[maxn];
 5 int f[maxn],s[maxn][2];
 6 void rot(int x){
 7     int y=f[x],z=f[y],l,r;
 8     l=s[y][0]==x?0:1,r=l^1;
 9     if(y!=rt) s[z][s[z][1]==y]=x;
10     f[x]=z,f[y]=x,f[s[x][r]]=s[x][r]!=0?y:0;
11     s[y][l]=s[x][r],s[x][r]=y;
12     sz[y]=sz[s[y][0]]+sz[s[y][1]]+num[y];
13     sz[x]=sz[s[x][0]]+sz[s[x][1]]+num[x];
14 }
15 void splay(int x){
16     int y,z;
17     while(x!=rt){
18         y=f[x],z=f[y];
19         if(y==rt) rot(x),rt=x;
20         else{
21             rot((s[z][0]==y)==(s[y][0]==x)?y:x),rot(x);
22             if(z==rt) rt=x;
23         }
24     }
25 }
26 void ins(int k,int x){
27     int fa=0;
28     while(k&&t[k]!=x) fa=k,++sz[k],k=s[k][x>t[k]];
29     if(t[k]==x) num[k]++,sz[k]++;
30     else{
31         k=s[fa][x>t[fa]]=++ts;
32         t[k]=x,sz[k]=1,f[k]=fa,num[k]=1;
33     }
34     splay(k);
35 }
36 void del(int k,int x){
37     while(t[k]!=x)
38     --sz[k],k=s[k][x>t[k]];
39     num[k]--,sz[k]--;
40     splay(k);
41     if(!num[k]){
42         rt=x=s[k][0];
43         while(s[x][1]) sz[x]+=sz[s[k][1]],x=s[x][1];
44         sz[x]+=sz[s[k][1]],s[x][1]=s[k][1],f[s[k][1]]=x;
45     }
46 }
47 int query(int k,int x){
48     while(t[k]!=x) k=s[k][x>t[k]];
49     splay(k);
50     return sz[s[k][0]];
51 }
52 int search(int k,int x){
53     if(x<=sz[s[k][0]]) return search(s[k][0],x);
54     if(x<=sz[s[k][0]]+num[k]) return t[k];
55     return search(s[k][1],x-sz[s[k][0]]-num[k]);
56 }
57 int in_x(int k,int x,int now){
58     if(!k) return now;
59     if(x>t[k]){
60         if(num[k]) return in_x(s[k][1],x,t[k]);
61         else return in_x(s[k][1],x,now);
62     }
63     else return in_x(s[k][0],x,now);
64 }
65 int ax_x(int k,int x,int now){
66     if(!k) return now;
67     if(x<t[k]){
68         if(num[k]) return ax_x(s[k][0],x,t[k]);
69         else return ax_x(s[k][0],x,now);
70     }
71     else return ax_x(s[k][1],x,now);
72 }
73 int n,opt,x;
74 int main(){
75     scanf("%d",&n);
76     rt=++ts,t[rt]=1e9,sz[rt]=1,num[rt]=1,ins(rt,-1e9);
77     for(int i=1;i<=n;i++){
78         scanf("%d%d",&opt,&x);
79         if(opt==1) ins(rt,x);
80         else if(opt==2) del(rt,x);
81         else if(opt==3) printf("%d\n",query(rt,x));
82         else if(opt==4) printf("%d\n",search(rt,x+1));
83         else if(opt==5) printf("%d\n",in_x(rt,x,0));
84         else if(opt==6) printf("%d\n",ax_x(rt,x,0));
85     }
86     return 0;
87 }

 

 

转载于:https://www.cnblogs.com/J-william/p/6940688.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值