ACM之路(20)—— Splay初探

  由于数据结构上老师讲了AVL树的rotate,然后去学了一下treap和Splay,这些数据结构还真是神奇啊!

  treap暂时只知道名次树的作用(就是一段动态变化的有序数列,找第K大的元素,用set显然是O(n)的。。)。

  好,正式介绍SplayTree这个神奇的数据结构:暂时的理解是,用于解决一些线段树解决不了的区间问题,比方说区间翻转,区间删除并插入等等(似乎分块也可以解决一些xjbg的区间问题)。。然后,Splay还可以解决掉LCT的问题(暂时还不会,,下次继续学习)。

  然后就愉快地掏出模板吧(直接修改了铭神的模板)。。仓鼠挂的平衡树

  G题,区间翻转+区间切割的。直接掏出模板套上去就是了:

  1 #include <stdio.h>
  2 #include <algorithm>
  3 #include <string.h>
  4 #define tochange (root->ch[1]->ch[0])
  5 using namespace std;
  6 const int N = 300000 + 100;
  7 struct node *nill, *root;
  8 struct node {
  9     int val;
 10     int sz;
 11     bool rev;
 12     node *ch[2],*fa;
 13     void init(int v = 0)
 14     {
 15         ch[0] = ch[1] = fa = nill;
 16         val = v;
 17         sz = 1;
 18     }
 19     void up() {
 20         if (this==nill) return ;
 21         sz = ch[0]->sz + ch[1]->sz + 1;
 22     }
 23     void down() {
 24         if (this==nill) return ;
 25         if(rev)
 26         {
 27             rev = false;
 28             ch[0]->rev ^= 1;
 29             ch[1]->rev ^= 1;
 30             swap(ch[0], ch[1]);
 31         }
 32     }
 33     bool d() {
 34         return fa->ch[1]==this;
 35     }
 36     void reverse() {
 37         rev ^= 1;
 38         std::swap(ch[0],ch[1]);
 39     }
 40     void setc(node *o,int c) {
 41         ch[c] = o;
 42         o->fa = this;
 43         up();
 44     }
 45     void rot() {
 46         int c = d(),cc = fa->d();
 47         node *z = fa->fa;
 48         node *tmp = fa;
 49         fa->setc(ch[c^1],c);
 50         setc(tmp,c^1);
 51         if(z!=nill) z->setc(this,cc);
 52         else fa = z;  // 这里的if-else不加也行,因为对于nill来说无论儿子是谁都无所谓,
 53                       // 并且,setc时的up对nill无效
 54     }
 55     void D() {
 56         if (this==nill) return ;
 57         fa->D();
 58         down();
 59     }
 60     void splay(node *aim = nill) {
 61         D();
 62         while (fa!=aim) {
 63             if (fa->fa!=aim) {
 64                 d()==fa->d() ? fa->rot() : rot();
 65             }
 66             rot();
 67         }
 68         if(aim == nill) root = this;
 69     }
 70 }memo[N], *bat;
 71
 72 node* findK(node* o, int k)
 73 {
 74     while(1)
 75     {
 76         o->down();
 77         if(o->ch[0]->sz + 1 == k)
 78         {
 79             return o;
 80         }
 81         if(o->ch[0]->sz >= k) o = o->ch[0];
 82         else
 83         {
 84             k -= o->ch[0]->sz + 1;
 85             o = o->ch[1];
 86         }
 87     }
 88 }
 89
 90 node* get_min(node* o)
 91 {
 92     /*while(o->ch[0] != nill)
 93     {
 94         o->down();
 95         o = o->ch[0];
 96     }
 97     return o;*/
 98     // 上面写法错了,为什么?
 99     // -因为可能o本来是没有左边的儿子的,一交换以后就有了,所以要先down().
100     o->down();
101     while(o->ch[0] != nill)
102     {
103         o = o->ch[0];
104         o->down();
105     }
106     return o;
107 }
108
109 void Reverse(int a,int b)
110 {
111     node* x = findK(root,a);
112     node* y = findK(root,b+2);
113     x->splay();
114     y->splay(root);
115     tochange->rev ^= 1;
116 }
117
118 void Cut(int a,int b,int c)
119 {
120     node* x = findK(root, a);
121     node* y = findK(root, b+2);
122     x->splay();
123     y->splay(root);
124     node* temp = tochange;
125     y->setc(nill, 0);
126     node* z = findK(root, c+1);
127     z->splay();
128     z = get_min(root->ch[1]);
129     z->splay(root);
130     root->ch[1]->setc(temp, 0);
131 }
132
133 int n,m;
134 node* newNode(int val = 0)
135 {
136     node* o = bat ++;
137     o->init(val);
138     return o;
139 }
140 void init()
141 {
142     bat = memo;
143     nill = newNode(); nill->sz = 0;
144     root = newNode(); root->fa = nill;
145     node* p = newNode(), *p2 = nill;
146     root->setc(p, 1);
147     p = nill;
148     for(int i=1;i<=n;i++)
149     {
150         p2 = newNode(i);
151         p2->setc(p, 0);
152         p = p2;
153     }
154     root->ch[1]->setc(p, 0);
155 }
156
157 int cnt = 0;
158 void print(node* o)
159 {
160     if(o == nill) return ;
161     o->down();
162     print(o->ch[0]);
163     if(cnt >= 1 && cnt <= n)
164     {
165         if(cnt > 1) printf(" ");
166         printf("%d",o->val);
167     }
168     cnt++;
169     print(o->ch[1]);
170 }
171
172 int main()
173 {
174     while(scanf("%d%d",&n,&m) == 2)
175     {
176         if(n == -1 && m == -1) break;
177         init();
178
179         char s[10];
180         while(m--)
181         {
182             scanf("%s",s);
183             if(s[0] == ‘F‘)
184             {
185                 int a,b;scanf("%d%d",&a,&b);
186                 Reverse(a,b);
187             }
188             else
189             {
190                 int a,b,c;
191                 scanf("%d%d%d",&a,&b,&c);
192                 Cut(a,b,c);
193             }
194         }
195         cnt = 0;
196         print(root);
197         puts("");
198     }
199 }

Splay区间翻转+区间切割

  然后A题,可以直接map写,然后弹出最大最小就可以了。为了练手Splay,特地用Splay写了一遍(惊喜的是时间比map的要短= =)。

  1 #include <stdio.h>
  2 #include <algorithm>
  3 #include <string.h>
  4 #define tochange (root->ch[1]->ch[0])
  5 using namespace std;
  6 const int N = 300000 + 100;
  7 struct node *nill, *root;
  8 struct node {
  9     int val, key;
 10     int sz;
 11     bool rev;
 12     node *ch[2],*fa;
 13     void init(int v,int p)
 14     {
 15         ch[0] = ch[1] = fa = nill;
 16         val = v;
 17         key = p;
 18         sz = 1;
 19     }
 20     void up() {
 21         if (this==nill) return ;
 22         sz = ch[0]->sz + ch[1]->sz + 1;
 23     }
 24     void down() {
 25         if (this==nill) return ;
 26         if(rev)
 27         {
 28             rev = false;
 29             ch[0]->rev ^= 1;
 30             ch[1]->rev ^= 1;
 31             swap(ch[0], ch[1]);
 32         }
 33     }
 34     bool d() {
 35         return fa->ch[1]==this;
 36     }
 37     void reverse() {
 38         rev ^= 1;
 39         std::swap(ch[0],ch[1]);
 40     }
 41     void setc(node *o,int c) {
 42         ch[c] = o;
 43         o->fa = this;
 44         up();
 45     }
 46     void rot() {
 47         int c = d(),cc = fa->d();
 48         node *z = fa->fa;
 49         node *tmp = fa;
 50         fa->setc(ch[c^1],c);
 51         setc(tmp,c^1);
 52         if(z!=nill) z->setc(this,cc);
 53         else fa = z;
 54     }
 55     void D() {
 56         if (this==nill) return ;
 57         fa->D();
 58         down();
 59     }
 60     void splay(node *aim = nill) {
 61         D();
 62         while (fa!=aim) {
 63             if (fa->fa!=aim) {
 64                 d()==fa->d() ? fa->rot() : rot();
 65             }
 66             rot();
 67         }
 68         if(aim == nill) root = this;
 69     }
 70 }memo[N], *bat;
 71
 72 node* newNode()
 73 {
 74     node* o = bat ++;
 75     return o;
 76 }
 77 void init()
 78 {
 79     bat = memo;
 80     nill = newNode(); nill->sz = 0;
 81     root = nill;
 82 }
 83
 84 void _insert(node* o, node* p)
 85 {
 86     if(p->key < o->key)
 87     {
 88         if(o->ch[0] == nill) {o->setc(p, 0);return ;}
 89         else _insert(o->ch[0], p);
 90     }
 91     else if(p->key > o->key)
 92     {
 93         if(o->ch[1] == nill) {o->setc(p, 1);return ;}
 94         else _insert(o->ch[1], p);
 95     }
 96 }
 97 void insert(int val,int key)
 98 {
 99     node* p = newNode();
100     p->init(val, key);
101     if(root == nill) root = p;
102     else _insert(root, p);
103 }
104
105 node* get_max_or_min(node* o, int op)
106 {
107     //o->down();
108     while(o->ch[op] != nill)
109     {
110         o = o->ch[op];
111         //o->down();
112     }
113     return o;
114 }
115 // 1->pop_max, 0->pop_min
116 void pop_max_or_min(int op)
117 {
118     if(root == nill) {printf("0\n");return;}
119     node* p = get_max_or_min(root, op);
120     p->splay(nill);
121     printf("%d\n",p->val);
122     root = p->ch[op^1];
123     root->fa = nill;
124 }
125
126 int main()
127 {
128     init();
129     int n;
130     while(scanf("%d",&n) == 1 && n)
131     {
132         if(n == 1)
133         {
134             int val, key;scanf("%d%d",&val,&key);
135             insert(val,key);
136         }
137         else if(n == 2) pop_max_or_min(1);
138         else pop_max_or_min(0);
139     }
140     return 0;
141 }

Splay维护最大或最小key的节点

  

  然后,,因为要复习了,暂时就放放了。。

时间: 2024-11-03 03:34:54

ACM之路(20)—— Splay初探的相关文章

OUC&amp;&amp;我的ACM之路(三)

OUC && 我的ACM之路(三) 时间匆匆,转眼间,省赛我都已经参加过三届了.前面两篇日志:OUC && 我的ACM之路(一)  OUC && 我的ACM之路(二)首先吐槽一下这次省赛题目实在太简单了,也许HIT认为我们就这个水平吧.SD加油,早日冲出final.  rank在这里 其实我也没啥好总结的,只是感概,时间过得匆匆,很多话,前面两篇里面已经说了.最后说一下,实际比赛的时候,ABG三个数学题目,ouc_abc写的,Orz,要当时的状态,我应该是推

hrbust 1331 ACM之路 邂逅DP【dp】

ACM之路 邂逅DP Time Limit: 1000 MS Memory Limit: 65536 K Total Submit: 158(51 users) Total Accepted: 67(46 users) Rating: Special Judge: No Description MM迷上了ACM,不可救药... 算法绝非一天两天就能学好,需要不断的看书学习,不断的敲代码,不断的看书学习,不断的敲代码... 这是一条没有尽头的路... 尼玛的搞ACM的伤不起啊!!! 详情见:搞AC

NOIP2012 借教室 Splay初探

终于把区间操作的Splay搞明白了…… Splay的大致框架是这样的: 1 template <class T> 2 struct SplayNode 3 { 4 typedef SplayNode<T> Node; 5 Node* lch; 6 Node* rch; 7 Node* parent; 8 T val; 9 10 SplayNode(const T& _val,Node* _parent): 11 lch(0),rch(0),parent(_parent),v

剪辑的楼天城的ACM之路

楼天城楼教主的acm心路历程(剪辑) 利用假期空闲之时,将这几年GCJ,ACM,TopCoder 参加的一些重要比赛作个回顾.昨天是GCJ2006 的回忆,今天时间上更早一些吧,我现在还清晰记得3 年前,我刚刚参加ACM 时参加北京赛区2005 和杭州赛区2005 的情况.2005 年ACM-ICPC——酸甜苦辣我进入清华大学开始本科学习的时间是2004 年8 月,在进入清华大学的第一年里,由于基础课学习比较紧张,再加上计算机系不允许大一学生自带电脑,我没有参加2004 年的ACM 比赛.不过在

C++之路进阶——splay树(宠物收养所)

1285 宠物收养所 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond 题目描述 Description 最近,阿Q开了一间宠物收养所.收养所提供两种服务:收养被主人遗弃的宠物和让新的主人领养这些宠物. 每个领养者都希望领养到自己满意的宠物,阿Q根据领养者的要求通过他自己发明的一个特殊的公式,得出该领养者希望领养的宠物的特点值a(a是一个正整数,a<2^31),而他也给每个处在收养所的宠物一个特点值.这样他就能够很方便的处理整个领养宠物的过程了,宠物收养所总

ACM:平衡树(2)——Splay

题目来源: HihoCoder1329 题目描述: 定义一个包含数字的集合,集合的初始情况为空.给定下面两种操作: 插入:向集合中添加一个数字k. 询问:询问集合中不超过k的最大数字. 删除:删除落在区间[a, b]内的所有数字. 题目要求对于每次询问,输出对应的答案. 解答:     本题和HihoCoder1325类似,可以用之前介绍的Treap算法来解答.但Treap树堆有一个问题,节点的权值是随机生成的,因此对树的调整操作也是随机发生的,在某些情况下,Treap树同样也可能会退化为一条线

cf386(div2)大一狗ACM之路

#cf386(div2)总结#前两题很顺利的做了出来, c题扔了, D题wrong了5发才A掉.A题签到题, 但是想多了, 代码写的有点长了. 找被整除最小值*7.B题 读题读了一会, 读完了就有思路了, 1A. 字符串问题, 从后往前两个两个的放到新的字符串里, 一个从最左, 一个从最右, 模拟指针扫着放, 最后特判会不会扫到一起.C题跳了没看, 最后做完了D题回来看了一眼没什么思路 日后再说.D题, 恩.. 两个多小时都用在这题上面了, 20分钟的时候做完了B之后就一直再啃D题, 暴力判断啊

acm之路--母函数 by小宇

母函数又叫生成函数,原是数学上的一个名词,是组合数学中的一个重要理论. 生成函数是说,构造这么一个多项式函数g(x).使得x的n次方系数为f(n). 对于母函数,看到最多的是这样两句话: 1."把组合问题的加法法则和幂级数的乘幂相应起来." 2."把离散数列和幂级数一 一相应起来,把离散数列间的相互结合关系相应成为幂级数间的运算关系.最后由幂级数形式来确定离散数列的构造. " 母函数能够解决一些问题 如 砝码问题,整数划分,等等 砝码问题: 有1克.2克.3克.4克

C#自学之路20

20.标签控件 标签主要用来显示文本,还可以显示图片,虽然参与窗口的Tab键顺序,但不接受焦点. 1.常用属性. a.Text属性,显示文本,可以用TextAlign设置文本对其方式. b.BorderStyle属性,设定标签的边框形式. c.BackColor属性,设定标签的背景色. d.ForeColor属性,设定标签的前景色. e.Font属性,设置字体的各种属性. f.Image属性,设定标签的背景图片. g.Enable和Visible,可用和可见属性. h.AutoSize,设定控件