进入splay tree的学习中;
据说splay tree在理论上功能十分强大,好好学;
splay首先一定是一棵BST,所以记不得的时候画个图就明白;
首先总结一下splay基本的操作左旋,右旋;
设节点x,其父节点y
左旋:保留x的右子树,y的左子树,将y插入到x的x的左子树上并原来x的左子树接到y的右子树上,右旋反之;
而Splay(x,s) (将x伸展为s的孩子)要分三种情况:
若y是根节点,则x在哪儿往相反方向旋(左孩子右旋,右孩子左旋)
若y的父节点是z,则若三点一线,先旋y再旋x
否则一直旋x,在哪儿反向旋;具体画个图就知道了;
核心就是在哪儿反向旋(是左孩子右旋,右孩子左旋)
然后分析一下splay的几个操作;
插入:很简单,按着BST的做法既可,之后把插入的元素伸展到根节点
删除:分类讨论,若要删除的节点x无孩子,那么直接删除(这不废话吗);
若x有1个孩子,那么把孩子接到父节点y上,然后删除;
若x有2个孩子,那么,就有点麻烦了;
首先找到x的后继p(第一个比x大的数),用其代替x,而本来p的孩子(只可能是有孩子),接到p父节点的左子树上
找K大数,这个跟BST一样
这三道平衡树的题目中,1503比较有思维难度(毕竟是noi)
有两个问题比较难处理 1.怎么修改 2.怎么删除
删除比较好办,把limit插入到tree中,然后删去左子树即可(注意:这里相等的值要插入到左子树中,原因见后文)
UPD:实际上相等的值处理应该在对应节点记录下出现次数即可
修改难住了我,一开始想的是想线段树一样加一个lazy域,可觉得删除,伸展时还要维护实在太烦;
后经人点醒,才明白完全可以只用一个全局变量temp来维护,
temp表示整个序列(不只是当前,当然好像也不好表示当前……)都修改的值,于是
相应的新加入的值就变为x-temp
附代码:
1 var lazy,a,fa,count:array[0..200010] of longint; 2 son:array[0..200010,0..2] of longint; 3 temp,m,n,lim,i,x,root,s:longint; 4 c:char; 5 6 procedure middle(i:longint); 7 begin 8 if son[i,1]<>0 then middle(son[i,1]); 9 write(a[i]+temp,' '); 10 if son[i,2]<>0 then middle(son[i,2]); 11 end; 12 13 procedure pushup(x:longint); //一种很简便的维护子树方法,不需要记得这么烦 14 begin 15 count[x]:=count[son[x,1]]+count[son[x,2]]+1; 16 end; 17 18 procedure rotate(x,f:longint); 19 var y,p:longint; 20 begin 21 y:=fa[x]; 22 fa[x]:=fa[y]; 23 if fa[y]<>0 then 24 begin 25 if son[fa[y],1]=y then son[fa[y],1]:=x 26 else son[fa[y],2]:=x; 27 end; 28 son[y,3-f]:=son[x,f]; 29 if son[x,f]<>0 then fa[son[x,f]]:=y; 30 fa[y]:=x; 31 son[x,f]:=y; 32 pushup(y); //注意顺序 33 pushup(x); 34 end; 35 36 procedure splay(x:longint); 37 var y:longint; 38 begin 39 while fa[x]<>0 do 40 begin 41 y:=fa[x]; 42 if fa[y]=0 then 43 begin 44 if son[y,1]=x then 45 rotate(x,2) 46 else rotate(x,1); 47 end 48 else if son[fa[y],1]=y then 49 begin 50 if son[y,1]=x then 51 begin 52 rotate(y,2); 53 rotate(x,2); 54 end 55 else begin 56 rotate(x,1); 57 rotate(x,2); 58 end; 59 end 60 else if son[fa[y],2]=y then 61 begin 62 if son[y,1]=x then 63 begin 64 rotate(x,2); 65 rotate(x,1); 66 end 67 else begin 68 rotate(y,1); 69 rotate(x,1); 70 end; 71 end; 72 end; 73 pushup(x); 74 root:=x; 75 end; 76 77 procedure insert(x:longint); 78 var p:longint; 79 begin 80 inc(m); 81 son[m,1]:=0; 82 son[m,2]:=0; 83 count[m]:=1; 84 a[m]:=x; 85 if root=0 then 86 begin 87 root:=m; 88 fa[m]:=0; 89 end 90 else begin 91 p:=root; 92 repeat 93 inc(count[p]); 94 if a[p]>=x then //注意:为什么要把等于的插入左子树,因为删除的时候删的是左子树,而如果比现在插入到右子树,根据之前的分析,将x旋转到根后,相等的父节点p会变成x的左子树(在哪就被旋转到相反到,相等时不需要删去) 95 begin 96 if son[p,1]=0 then break; 97 p:=son[p,1]; 98 end 99 else begin 100 if son[p,2]=0 then break; 101 p:=son[p,2]; 102 end; 103 until false; 104 fa[m]:=p; 105 if a[p]>=x then son[p,1]:=m else son[p,2]:=m; 106 splay(m); 107 end; 108 end; 109 110 function kth(x:longint):longint; 111 var p,h:longint; 112 begin 113 p:=root; 114 h:=x; 115 while h<>count[son[p,2]]+1 do 116 begin 117 if h>count[son[p,2]]+1 then 118 begin 119 h:=h-count[son[p,2]]-1; 120 p:=son[p,1]; 121 end 122 else p:=son[p,2]; 123 end; 124 exit(p); 125 end; 126 127 begin 128 readln(n,lim); 129 temp:=0; 130 for i:=1 to n do 131 begin 132 readln(c,x); 133 if c='I' then 134 begin 135 if x>=lim then 136 begin 137 insert(x-temp); 138 s:=s+1; 139 end; 140 end 141 else if c='S' then 142 begin 143 temp:=temp-x; 144 insert(lim-temp); 145 if son[m,2]<>0 then 146 begin 147 root:=son[m,2]; 148 fa[son[m,2]]:=0; 149 m:=m-1; 150 end 151 else begin 152 root:=0; 153 fillchar(fa,sizeof(fa),0); 154 fillchar(count,sizeof(count),0); 155 fillchar(son,sizeof(son),0); 156 m:=0; 157 end; 158 end 159 else if c='A' then temp:=temp+x 160 else if c='F' then 161 if x>count[root] then writeln(-1) else writeln(a[kth(x)]+temp); //注意打印的时候不忘+temp 162 // middle(root); 163 end; 164 writeln(s-count[root]); //count[root]就代表了整棵树的规模,即现有的人数 165 end.
另外附删除节点编号为x的子程序;
1 function find(i,f:longint):longint; 2 var p:longint; 3 begin 4 p:=son[i,f]; 5 while son[p,3-f]<>0 do p:=son[p,3-f]; //后缀:右孩子的最左节点 6 find:=p; 7 end; 8 9 procedure delete(i:longint); 10 var p,q:longint; 11 begin 12 dec(t); 13 if t=0 then 14 begin 15 root:=0; 16 f:=-1; 17 exit; 18 end; 19 p:=0; 20 if son[fa[i],1]=i then p:=1 21 else if son[fa[i],2]=i then p:=2; 22 if (son[i,1]<>0) and (son[i,2]<>0) then 23 begin 24 q:=find(i,2); 25 if root=i then root:=q; 26 if fa[q]<>i then //当后继就是x的左孩子时,后继的孩子依然接在后继上 27 begin 28 son[fa[q],1]:=son[q,2]; 29 fa[son[q,2]]:=fa[q]; 30 end; 31 fa[q]:=fa[i]; 32 son[fa[i],p]:=q; 33 son[q,1]:=son[i,1]; 34 fa[son[q,1]]:=q; 35 if son[i,2]<>q then //小细节 36 begin 37 son[q,2]:=son[i,2]; 38 fa[son[q,2]]:=q; 39 end; 40 fa[i]:=-1; 41 son[i,1]:=0; 42 son[i,2]:=0; 43 end 44 else begin 45 q:=0; 46 if son[i,1]<>0 then q:=1; 47 if son[i,2]<>0 then q:=2; 48 if root=i then root:=son[i,q]; 49 if q=0 then son[fa[i],p]:=0 50 else begin 51 fa[son[i,q]]:=fa[i]; 52 son[fa[i],p]:=son[i,q]; 53 end; 54 fa[i]:=-1; 55 end; 56 end;