传说中的平衡树
学了好久了,最开始用的是Treap,现在又学了splay,还没有掌握,先做个初步总结,算是准备个模板吧。
前几天写了个splay的题,使用的以前学的splay,巨丑无比
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /* 2 排名系统 -- Treap 3 */ 4 5 #include <map> 6 #include <cmath> 7 #include <cstdio> 8 #include <cstdlib> 9 #include <iostream> 10 #include <ctime> 11 12 #define MaxN 50000 13 #define oo 100000000 14 #define lld long long 15 16 using namespace std; 17 18 struct node 19 { 20 int l,r; 21 int key; 22 int fa; 23 }a[MaxN]; 24 25 int tot,root,n,num; 26 lld ans; 27 28 //左旋 29 void turn_left(int x) 30 { 31 int y=a[x].fa; 32 int z=a[y].fa; 33 int son=a[x].l; 34 if (a[z].l==y) a[z].l=x; 35 else a[z].r=x; 36 a[x].fa=z; 37 a[x].l=y; a[y].fa=x; 38 a[y].r=son; a[son].fa=y; 39 } 40 //右旋 41 void turn_right(int x) 42 { 43 int y=a[x].fa; 44 int z=a[y].fa; 45 int son=a[x].r; 46 if (a[z].l==y) a[z].l=x; else a[z].r=x; 47 a[x].fa=z; 48 a[x].r=y; a[y].fa=x; 49 a[y].l=son; a[son].fa=y; 50 } 51 52 //Splay 53 void Splay(int &root,int x) 54 { 55 int y,z; 56 //旋转至根节点 57 while (x!=root) 58 { 59 y=a[x].fa; z=a[y].fa; 60 61 if (y==root) { //父节点为根 62 if (a[y].l==x) turn_right(x); 63 else turn_left(x); 64 root=x; 65 } else { 66 67 if (a[z].l==y) 68 if (a[y].l==x) 69 { //同为左儿子 70 turn_right(y); 71 turn_right(x); 72 } 73 else { //y左 x右 74 turn_left(x); 75 turn_right(x); 76 } 77 78 if (a[z].r==y) 79 if (a[y].r==x) 80 { //同为右儿子 81 turn_left(y); 82 turn_left(x); 83 } 84 else { //y右 x左 85 turn_right(x); 86 turn_left(x); 87 } 88 } 89 if (root==z) root=x; 90 } 91 } 92 //插入 93 void insert(int &k,int tkey,int fa) 94 { 95 if (k==-1) 96 { 97 k=++tot; 98 a[k].l=a[k].r=-1; 99 a[k].fa=fa; 100 a[k].key=tkey; 101 Splay(root,k); 102 } 103 else { 104 if (tkey < a[k].key) insert(a[k].l,tkey,k); 105 else insert(a[k].r,tkey,k); 106 } 107 } 108 109 lld Max(int t) 110 { 111 if (t==-1) return oo; 112 if (a[t].r!=-1) return Max(a[t].r); 113 else return a[t].key; 114 } 115 116 lld Min(int t) 117 { 118 if (t==-1) return oo; 119 if (a[t].l!=-1) return Min(a[t].l); 120 else return a[t].key; 121 } 122 123 int main() 124 { 125 126 127 srand(time(0)); 128 /*for (int i=0; i<=MaxN; i++) 129 a[i].l=a[i].r=-1; */ 130 root=-1; tot=0; 131 132 scanf("%d",&n); 133 134 int x; 135 scanf("%d",&x); 136 insert(root,x,-1); 137 ans=x; 138 139 for (int i=2; i<=n; i++) 140 { 141 scanf("%d",&x); 142 insert(root,x,-1); 143 lld t1 = Max(a[root].l); //最大前驱 144 lld t2 = Min(a[root].r); //最小后继 145 ans += min(abs(x-t1),abs(x-t2)); 146 } 147 148 printf("%I64d\n",ans); 149 150 151 return 0; 152 } 153 154
在之后我学习了另一种写法,比较好,就放弃了原来的了。
①首先是数据结构的定义
1 struct node { 2 int v,c[2],p,sz,mul; 3 bool d; 4 } T[MaxN];
v表示值,c为左右儿子(0左1右),p表示父节点,mul表示相同值的个数,d表示自己是左儿子还是有儿子。
②旋转操作
对x点操作,y为父节点,分为左旋(ZAG)和右旋(ZIG)
右旋(ZIG):如果某个非根结点X是其父结点Y的左子结点,则可以通过右旋操作将X旋转到Y的位置,即:先将Y的左子结点设为X的右子结点,再将X的右子结点设为Y;
左旋(ZAG):如果某个非根结点X是其父结点Y的右子结点,则可以通过左旋操作将X旋转到Y的位置,即:先将Y的右子结点设为X的左子结点,再将X的左子结点设为Y;
可以将两种旋转定义成一种操作:
1 void rot(int x) 2 { 3 int y=T[x].p,d=T[x].d; 4 if (y==root) { root = x; T[root].p=0; } 5 else sc(T[y].p,x,T[y].d); 6 sc(y,T[x].c[!d],d); sc(x,y,!d); 7 upd(y); upd(x); 8 }
其中sc(p,x,d)表示的是把p的d号子结点变成x,也就是如下
1 //p的d子树变成c 2 int sc(int _p,int _c,bool _d) 3 { 4 T[_p].c[_d]=_c; T[_c].p=_p; T[_c].d=_d; 5 }
③updata操作
当前结点改变后,要更新x的一些域,就用他更新了,基本上每题都不一样的,所以就不贴了。
④splay操作
Splay(x, r)表示将x伸展到r的子结点处,若r=0,则表示伸展到根(因为根的父结点为T[0])。过程如下:
(1)设x的父结点为p。若p的父结点即是r,则rot(x);
(2)若p的父结点不是r且T[x].d=T[p].d,则先rot(p)再rot(x);
(3)若p的父结点不是r且T[x].d!=T[p].d,则两次rot(x);
(4)重复以上过程直到x的父结点为r;
1 void splay(int x,int r) 2 { 3 int i=x,p,p0; 4 while ( (p0=T[i].p) != r) 5 { 6 p = T[p0].p; 7 if (p==r) rot(i); 8 else if (T[i].d==T[p0].d) { rot(p0); rot(i); } 9 else { rot(i); rot(i); }; 10 } 11 }
⑤插入操作
ins(_v)表示在树中插入一个值为_v的结点。由于树是否为空的问题以及mul的引入,插入操作有三种可能结果:
(1)树为空(根结点为0):此时将插入一个新的结点,值为_v,初始sz、mul值均为1,并将其作为根结点;
(2)树非空且值为_v的结点在树中不存在:此时将插入一个新的结点,值为_v,初始sz、mul值均为1;
(3)树非空且值为_v的结点在树中已存在:此时会将树中这个值为_v的结点的mul值加1;
1 void ins(int _v) 2 { 3 //树为空 4 if (!root) { 5 T[++n].v = _v; 6 T[n].c[0] = T[n].c[1] = T[n].p =0; 7 T[n].sz = T[n].mul =1; root = n; 8 return; 9 } 10 int i=root,j; 11 while (1) { 12 T[i].sz++; 13 if (T[i].v == _v) { T[i].mul++; splay(i,0); return; } 14 j = T[i].c[ _v > T[i].v]; 15 if (!j) break; 16 else i=j; 17 } 18 T[++n].v = _v; T[n].c[0] = T[n].c[1] = 0; T[n].sz = T[n].mul = 1; 19 sc(i , n , _v > T[i].v); 20 splay(n,0); 21 }
其余的操作就不再写了、例如删除什么的,等程序里用到了参考程序吧。
先贴一道题,好久前老师就让我写了
用到的基本操作比较多,当做模板来写的,算是用来适应splay
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 *Prob : NOI 2004--cashier 3 *Data : 2012-6 4 *Sol : Splay 5 *Author : ZhouHang 6 */ 7 8 #include <cstdio> 9 #include <cstring> 10 11 #define MaxN 100001 12 #define oo 1000000000 13 14 using namespace std; 15 16 //0--左 1--右 左小右大 17 struct node { 18 int v,c[2],p,sz,mul; 19 bool d; 20 } T[MaxN]; 21 22 int n=0,root,res,tot=0; 23 24 void upd(int x) 25 { 26 T[x].sz = T[T[x].c[0]].sz + T[T[x].c[1]].sz + T[x].mul; 27 } 28 //p的d子树变成c 29 int sc(int _p,int _c,bool _d) 30 { 31 T[_p].c[_d]=_c; T[_c].p=_p; T[_c].d=_d; 32 } 33 void rot(int x) 34 { 35 int y=T[x].p,d=T[x].d; 36 if (y==root) { root = x; T[root].p=0; } 37 else sc(T[y].p,x,T[y].d); 38 sc(y,T[x].c[!d],d); sc(x,y,!d); 39 upd(y); upd(x); 40 } 41 void splay(int x,int r) 42 { 43 int i=x,p,p0; 44 while ( (p0=T[i].p) != r) 45 { 46 p = T[p0].p; 47 if (p==r) rot(i); 48 else if (T[i].d==T[p0].d) { rot(p0); rot(i); } 49 else { rot(i); rot(i); }; 50 } 51 } 52 void ins(int _v) 53 { 54 //树为空 55 if (!root) { 56 T[++n].v = _v; 57 T[n].c[0] = T[n].c[1] = T[n].p =0; 58 T[n].sz = T[n].mul =1; root = n; 59 return; 60 } 61 int i=root,j; 62 while (1) { 63 T[i].sz++; 64 if (T[i].v == _v) { T[i].mul++; splay(i,0); return; } 65 j = T[i].c[ _v > T[i].v]; 66 if (!j) break; 67 else i=j; 68 } 69 T[++n].v = _v; T[n].c[0] = T[n].c[1] = 0; T[n].sz = T[n].mul = 1; 70 sc(i , n , _v > T[i].v); 71 splay(n,0); 72 } 73 void del(int limit) 74 { 75 if (!root) return; 76 int i=root, b=0, _min=oo, v0; 77 //找到大于等于limit最小的 78 while (i) { 79 v0 = T[i].v; 80 if (v0==limit) {b=i; break;} 81 if (v0<limit) i = T[i].c[1]; 82 else { 83 if ( v0 < _min) { _min =v0; b=i; } 84 i=T[i].c[0]; 85 } 86 } 87 //删除比v[b]小的所有节点 88 if (!b) { tot += T[root].sz; root = 0; } 89 else { 90 splay(b,0); 91 tot += T[T[root].c[0]].sz; 92 T[T[root].c[0]].p=0; 93 T[root].c[0]=0; 94 upd(root); 95 } 96 } 97 int Find_Kth(int K) 98 { 99 int i = root, s0, m0; 100 while (1) { 101 s0 = T[T[i].c[0]].sz; m0 = T[i].mul; 102 if (K <= s0) i = T[i].c[0]; 103 else if ( K<=s0+m0 ) return T[i].v; 104 else { K -= s0+m0; i = T[i].c[1];} 105 } 106 } 107 108 int main() 109 { 110 freopen("cashier.in", "r", stdin); 111 freopen("cashier.out", "w", stdout); 112 113 int m,minv,delta=0,x; 114 char ch; 115 scanf("%d%d%*c",&m,&minv); 116 117 for (int i=1; i<=m; i++) 118 { 119 scanf("%c%d%*c",&ch,&x); 120 switch (ch) { 121 case 'I' : { if (x>=minv) ins(x-delta); break; } 122 case 'A' : { delta+=x; break; } 123 case 'S' : { delta-=x; del(minv-delta); break; } 124 case 'F' : if (T[root].sz-x+1<=0) printf("-1\n"); 125 else printf("%d\n",Find_Kth(T[root].sz-x+1)+delta); 126 } 127 } 128 129 printf("%d\n",tot); 130 131 fclose(stdin); fclose(stdout); 132 return 0; 133 }