上次ZOJ月赛碰到一个题目要求对序列中的某个区间求gcd,并且还要随时对某位数字进行修改 插入 删除,当时马上联想到线段树,但是线段树不支持增删,明显还是不可以的,然后就敲了个链表想暴力一下,结果TLE。那天回来后搜了下题解,发现大家都在说平衡树 Splay,就好好学了下,这玩意还是挺难学的,我看了好久。最后还是从网上找了三篇论文打印了下,趁着TCG讲数据库的时候(这课真的好催眠)好好看了下,才搞清楚基本的Splay操作

这是第一道Splay题目,基本上是照着模板敲出来的,没办法,第一次学,好多地方不熟练,不过整个过程我已经形成了一个条理了,这倒是一大收获

由于自己的粗心,好几个细节错了,调试了好久。。Sigh!

  1. #include <cstdio>
  2. #include <cstring>
  3. #include <algorithm>
  4. #define N 150010
  5. using namespace std;
  6. struct Node
  7. {
  8. Node *ch[],*pre;
  9. int val,size;
  10. bool rev;
  11. void update()
  12. {
  13. size=ch[]->size+ch[]->size+;
  14. }
  15. void pushdown();
  16. }Tnull,*null=&Tnull;//这里要把null设置为指针,由于下面基本上用的指针,有大量判断是否相等,必须统一类型。
  17. void Node::pushdown()//该函数涉及null,不能写在结构体里,否则被认为null未被定义,只能用写在外面这样的方式来实现
  18. {
  19. if (rev){
  20. swap(ch[],ch[]);
  21. for (int i=;i<;i++)
  22. if (ch[i]!=null) ch[i]->rev=!ch[i]->rev;
  23. rev=;
  24. }
  25. }
  26. Node nodePool[N];
  27. Node *root,*cur;
  28. int n,m;
  29. Node * newNode(int v,Node* f) //从点池中生成新的点
  30. {
  31. cur->ch[]=cur->ch[]=null;
  32. cur->size=;
  33. cur->val=v;
  34. cur->rev=;
  35. cur->pre=f;
  36. return cur++;
  37. }
  38. Node*build(int l,int r,Node* f)//建树操作。
  39. {
  40. if (l>r) return null;
  41. int mid=(l+r)>>;
  42. //printf("%d %d\n",l,r);
  43. Node* temp=newNode(mid,f);
  44. temp->ch[]=build(l,mid-,temp);
  45. temp->ch[]=build(mid+,r,temp);
  46. temp->update();
  47. return temp;
  48.  
  49. }
  50. void rotate(Node* x,int c) //旋转操作,分情况讨论下就行
  51. {
  52. Node* y=x->pre;
  53. y->pushdown();
  54. x->pushdown();
  55. // puts("judge1");
  56. y->ch[!c]=x->ch[c];
  57. if (x->ch[c]!=null)
  58. x->ch[c]->pre=y;
  59. x->pre=y->pre;
  60. // puts("judge2");
  61. if (y->pre!=null){
  62. // puts("deep1");
  63. //if (y->pre==null) puts("Yes");
  64. //printf("%d\n",y->pre->size);
  65. if (y->pre->ch[]==y){
  66. //puts("deep2");
  67.  
  68. y->pre->ch[]=x;
  69. // puts("deep3");
  70. }
  71. else{
  72. // puts("deep4");
  73. y->pre->ch[]=x;
  74. // puts("deep5");
  75. }
  76. }
  77. // puts("judge3");
  78. x->ch[c]=y;
  79. y->pre=x;
  80. y->update();
  81. x->update();
  82. if (y==root)
  83. root=x;
  84. //puts("is there?");
  85. }
  86. void Splay(Node* x,Node* f)//Splay过程说白了就是把x节点旋转到f下面,分情况讨论一下就可以
  87. {
  88. x->pushdown();
  89. while (x->pre!=f)
  90. {
  91. //puts("Why?");
  92. if (x->pre->pre==f)
  93. {
  94. if (x->pre->ch[]==x)
  95. rotate(x,);
  96. else
  97. rotate(x,);
  98. // puts("pass1");
  99. }
  100. else
  101. {
  102. //puts("pass2");
  103. Node *y=x->pre;
  104. Node *z=y->pre;
  105. if (z->ch[]==y)
  106. {
  107. // puts("pass3");
  108. if (y->ch[]==x)
  109. rotate(y,),rotate(x,);
  110. else
  111. rotate(x,),rotate(x,);
  112. // puts("pass4");
  113. }
  114. else
  115. {
  116. // puts("pass5");
  117. //rotate(y,1);
  118. if (y->ch[]==x){
  119. // puts("pass6");
  120. rotate(x,);
  121. rotate(x,);
  122. //puts("pass7");
  123. }
  124. else
  125. {
  126. // puts("pass8");
  127. rotate(y,),rotate(x,);
  128. }
  129. // puts("pass10");
  130. }
  131. //puts("pass7");
  132. }
  133. // puts("isblock?");
  134. }
  135. x->update();
  136. }
  137. void select(int k,Node* f) //把第k个节点旋转到f的下面
  138. {
  139. int tmp;
  140. Node* x=root;
  141. //x->pushdown();
  142. k++; //因为建树的时候插入了0点作为缓冲点,因此实际的点要++以消除该点的影响。
  143. for (;;){
  144. x->pushdown();
  145. tmp=x->ch[]->size;
  146. if (k==tmp+) break;
  147. if (k<=tmp)
  148. x=x->ch[];
  149. else{
  150. k-=tmp+;
  151. x=x->ch[];
  152. }
  153. }
  154. //puts("Test");
  155. Splay(x,f);
  156. // puts("t");
  157. }
  158. Node* get(int l,int r)
  159. {
  160. select(l-,null);
  161. //puts("pass");
  162. select(r+,root);
  163. // puts("pass");
  164. return root->ch[]->ch[];
  165. }
  166. void reverses(int a,int b) //翻转操作
  167. {
  168. Node* o=get(a,b);
  169. //puts("Pass");
  170. o->rev=!o->rev;
  171. Splay(o,null);
  172.  
  173. }
  174. void split(int l,int r,Node* &s)//切除翻转序列,并把切除的序列用s保存起来
  175. {
  176. Node*temp=get(l,r);
  177. root->ch[]->ch[]=null;
  178. root->ch[]->update();
  179. root->update();
  180. s=temp;
  181. }
  182. void cut(int l,int r) //把切除的序列接到序列末端,只需把序列的最右边点移到根节点,再设置它的右孩子为刚刚切除的序列即可
  183. {
  184. Node *tmp;
  185. split(l,r,tmp);
  186. select(root->size-,null);//把自己手动设置的第n+1点移植到顶点,这样做的原因是防止他干扰,不移它的话,最右点就不是第n点,而是n+1点了
  187. select(root->size-,root);
  188. root->ch[]->ch[]=tmp;
  189. tmp->pre=root->ch[];
  190. root->ch[]->update();
  191. root->update();
  192. }
  193. void init(int num)
  194. {
  195. cur=nodePool;
  196. root=null;
  197. root=newNode(,null);
  198. root->ch[]=newNode(num+,root);//设置两个缓冲点,我刚刚还在犹豫n+1点是否需要,经实测确实需要,因为程序中随时要探测自己的子孩子,如果不设置,可能会溢出
  199. root->ch[]->ch[]=build(,num,root->ch[]);
  200. root->update();//
  201. }
  202. void output()
  203. {
  204. for (int i=;i<=n;i++)
  205. {
  206. select(i,null); //输出某点即把该点旋转到根节点再输出
  207. printf("%d\n",root->val);
  208. }
  209. }
  210. int main()
  211. {
  212. int a,b;
  213. while (scanf("%d%d",&n,&m)!=EOF)
  214. {
  215. init(n);
  216. //output();
  217. while (m--)
  218. {
  219. scanf("%d%d",&a,&b);
  220. if (a>b) swap(a,b);
  221. reverses(a,b);
  222. cut(a,b);
  223. }
  224. output();
  225. }
  226. }

UVA 11922 伸展树Splay 第一题的更多相关文章

  1. ZOJ 3765 Lights (zju March I)伸展树Splay

    ZJU 三月月赛题,当时见这个题目没辙,没学过splay,敲了个链表TLE了,所以回来好好学了下Splay,这道题目是伸展树的第二题,对于伸展树的各项操作有了更多的理解,这题不同于上一题的用指针表示整 ...

  2. 树-伸展树(Splay Tree)

    伸展树概念 伸展树(Splay Tree)是一种二叉排序树,它能在O(log n)内完成插入.查找和删除操作.它由Daniel Sleator和Robert Tarjan创造. (01) 伸展树属于二 ...

  3. 纸上谈兵: 伸展树 (splay tree)[转]

    作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢!  我们讨论过,树的搜索效率与树的深度有关.二叉搜索树的深度可能为n,这种情况下,每 ...

  4. K:伸展树(splay tree)

      伸展树(Splay Tree),也叫分裂树,是一种二叉排序树,它能在O(lgN)内完成插入.查找和删除操作.在伸展树上的一般操作都基于伸展操作:假设想要对一个二叉查找树执行一系列的查找操作,为了使 ...

  5. 高级搜索树-伸展树(Splay Tree)

    目录 局部性 双层伸展 查找操作 插入操作 删除操作 性能分析 完整源码 与AVL树一样,伸展树(Splay Tree)也是平衡二叉搜索树的一致,伸展树无需时刻都严格保持整棵树的平衡,也不需要对基本的 ...

  6. UVA 11922 Permutation Transformer —— splay伸展树

    题意:根据m条指令改变排列1 2 3 4 … n ,每条指令(a, b)表示取出第a~b个元素,反转后添加到排列尾部 分析:用一个可分裂合并的序列来表示整个序列,截取一段可以用两次分裂一次合并实现,粘 ...

  7. [Splay伸展树]splay树入门级教程

    首先声明,本教程的对象是完全没有接触过splay的OIer,大牛请右上角.. 首先引入一下splay的概念,他的中文名是伸展树,意思差不多就是可以随意翻转的二叉树 PS:百度百科中伸展树读作:BoGa ...

  8. 伸展树Splay【非指针版】

    ·伸展树有以下基本操作(基于一道强大模板题:codevs维护队列): a[]读入的数组;id[]表示当前数组中的元素在树中节点的临时标号;fa[]当前节点的父节点的编号;c[][]类似于Trie,就是 ...

  9. POJ 3580 - SuperMemo - [伸展树splay]

    题目链接:http://poj.org/problem?id=3580 Your friend, Jackson is invited to a TV show called SuperMemo in ...

随机推荐

  1. IDEA开发spring boot应用时 application.yml 或 application.properties 自定义属性提示

    在使用spring boot开发过程中,经常会定义一些应用自己的属性,直接写到application配置文件中使用@Value注解进行使用,这样使用也没有什么问题.不过我认为更优雅的方式是定义自己的属 ...

  2. http error 502.5

    原文地址:https://www.cnblogs.com/loui/p/7826073.html 在部署网站时遇到的各种问题,通过检索找到了解决方案,感谢!!记录一下以免忘记.. 解决方法:把IIS的 ...

  3. 吴裕雄 Bootstrap 前端框架开发——Bootstrap 字体图标(Glyphicons):glyphicon glyphicon-time

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name ...

  4. Linux学习《第四章脚本》20200222

  5. P1083 是否存在相等的差

    P1083 是否存在相等的差 转跳点:

  6. netty权威指南学习笔记一——NIO入门(3)NIO

    经过前面的铺垫,在这一节我们进入NIO编程,NIO弥补了原来同步阻塞IO的不足,他提供了高速的.面向块的I/O,NIO中加入的Buffer缓冲区,体现了与原I/O的一个重要区别.在面向流的I/O中,可 ...

  7. oracle 开发注意事项

    新建表或字段时,不能使用char,统一使用varcha,防止判断null时有遗漏 新建表,索引,序列,新增删除或修改字段的时候,要先判断操作的对象是否存在,否则SLQ会报错 插入或者修改特殊字符,解决 ...

  8. Python 中使用 ddt 来进行数据驱动,批量执行用例,修改ddt代码

    1. 什么是数据驱动? 使用数据驱动有什么好处? 用例执行是靠数据来驱动的,每条测试用例除了测试数据不一样意外,所有的用例代码都是一样的,为了使用例批量执行,我们会使用数据驱动的思想来批量执行测试用例 ...

  9. 关于springmvc的消息转换器

    之前有用到消息转换器,一直是配置configureMessageConverters()这个方法的,虽然知道也有extendMessageConverters().它们的区别的是第一个不会继承框架默认 ...

  10. mybatis基础CURD的学习

    <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "- ...