传说中的平衡树

  学了好久了,最开始用的是Treap,现在又学了splay,还没有掌握,先做个初步总结,算是准备个模板吧。

 

前几天写了个splay的题,使用的以前学的splay,巨丑无比

[HNOI2002]营业额统计

HNOI2002
  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 }

 

其余的操作就不再写了、例如删除什么的,等程序里用到了参考程序吧。

 

先贴一道题,好久前老师就让我写了

 [NOI2004]郁闷的出纳员

用到的基本操作比较多,当做模板来写的,算是用来适应splay

NOI2004--郁闷的出纳员
  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 }


 

posted @ 2012-06-24 21:43  守護N1身边  阅读(158)  评论(0编辑  收藏  举报