UVA 11922 伸展树Splay 第一题
上次ZOJ月赛碰到一个题目要求对序列中的某个区间求gcd,并且还要随时对某位数字进行修改 插入 删除,当时马上联想到线段树,但是线段树不支持增删,明显还是不可以的,然后就敲了个链表想暴力一下,结果TLE。那天回来后搜了下题解,发现大家都在说平衡树 Splay,就好好学了下,这玩意还是挺难学的,我看了好久。最后还是从网上找了三篇论文打印了下,趁着TCG讲数据库的时候(这课真的好催眠)好好看了下,才搞清楚基本的Splay操作
这是第一道Splay题目,基本上是照着模板敲出来的,没办法,第一次学,好多地方不熟练,不过整个过程我已经形成了一个条理了,这倒是一大收获
由于自己的粗心,好几个细节错了,调试了好久。。Sigh!
- #include <cstdio>
- #include <cstring>
- #include <algorithm>
- #define N 150010
- using namespace std;
- struct Node
- {
- Node *ch[],*pre;
- int val,size;
- bool rev;
- void update()
- {
- size=ch[]->size+ch[]->size+;
- }
- void pushdown();
- }Tnull,*null=&Tnull;//这里要把null设置为指针,由于下面基本上用的指针,有大量判断是否相等,必须统一类型。
- void Node::pushdown()//该函数涉及null,不能写在结构体里,否则被认为null未被定义,只能用写在外面这样的方式来实现
- {
- if (rev){
- swap(ch[],ch[]);
- for (int i=;i<;i++)
- if (ch[i]!=null) ch[i]->rev=!ch[i]->rev;
- rev=;
- }
- }
- Node nodePool[N];
- Node *root,*cur;
- int n,m;
- Node * newNode(int v,Node* f) //从点池中生成新的点
- {
- cur->ch[]=cur->ch[]=null;
- cur->size=;
- cur->val=v;
- cur->rev=;
- cur->pre=f;
- return cur++;
- }
- Node*build(int l,int r,Node* f)//建树操作。
- {
- if (l>r) return null;
- int mid=(l+r)>>;
- //printf("%d %d\n",l,r);
- Node* temp=newNode(mid,f);
- temp->ch[]=build(l,mid-,temp);
- temp->ch[]=build(mid+,r,temp);
- temp->update();
- return temp;
- }
- void rotate(Node* x,int c) //旋转操作,分情况讨论下就行
- {
- Node* y=x->pre;
- y->pushdown();
- x->pushdown();
- // puts("judge1");
- y->ch[!c]=x->ch[c];
- if (x->ch[c]!=null)
- x->ch[c]->pre=y;
- x->pre=y->pre;
- // puts("judge2");
- if (y->pre!=null){
- // puts("deep1");
- //if (y->pre==null) puts("Yes");
- //printf("%d\n",y->pre->size);
- if (y->pre->ch[]==y){
- //puts("deep2");
- y->pre->ch[]=x;
- // puts("deep3");
- }
- else{
- // puts("deep4");
- y->pre->ch[]=x;
- // puts("deep5");
- }
- }
- // puts("judge3");
- x->ch[c]=y;
- y->pre=x;
- y->update();
- x->update();
- if (y==root)
- root=x;
- //puts("is there?");
- }
- void Splay(Node* x,Node* f)//Splay过程说白了就是把x节点旋转到f下面,分情况讨论一下就可以
- {
- x->pushdown();
- while (x->pre!=f)
- {
- //puts("Why?");
- if (x->pre->pre==f)
- {
- if (x->pre->ch[]==x)
- rotate(x,);
- else
- rotate(x,);
- // puts("pass1");
- }
- else
- {
- //puts("pass2");
- Node *y=x->pre;
- Node *z=y->pre;
- if (z->ch[]==y)
- {
- // puts("pass3");
- if (y->ch[]==x)
- rotate(y,),rotate(x,);
- else
- rotate(x,),rotate(x,);
- // puts("pass4");
- }
- else
- {
- // puts("pass5");
- //rotate(y,1);
- if (y->ch[]==x){
- // puts("pass6");
- rotate(x,);
- rotate(x,);
- //puts("pass7");
- }
- else
- {
- // puts("pass8");
- rotate(y,),rotate(x,);
- }
- // puts("pass10");
- }
- //puts("pass7");
- }
- // puts("isblock?");
- }
- x->update();
- }
- void select(int k,Node* f) //把第k个节点旋转到f的下面
- {
- int tmp;
- Node* x=root;
- //x->pushdown();
- k++; //因为建树的时候插入了0点作为缓冲点,因此实际的点要++以消除该点的影响。
- for (;;){
- x->pushdown();
- tmp=x->ch[]->size;
- if (k==tmp+) break;
- if (k<=tmp)
- x=x->ch[];
- else{
- k-=tmp+;
- x=x->ch[];
- }
- }
- //puts("Test");
- Splay(x,f);
- // puts("t");
- }
- Node* get(int l,int r)
- {
- select(l-,null);
- //puts("pass");
- select(r+,root);
- // puts("pass");
- return root->ch[]->ch[];
- }
- void reverses(int a,int b) //翻转操作
- {
- Node* o=get(a,b);
- //puts("Pass");
- o->rev=!o->rev;
- Splay(o,null);
- }
- void split(int l,int r,Node* &s)//切除翻转序列,并把切除的序列用s保存起来
- {
- Node*temp=get(l,r);
- root->ch[]->ch[]=null;
- root->ch[]->update();
- root->update();
- s=temp;
- }
- void cut(int l,int r) //把切除的序列接到序列末端,只需把序列的最右边点移到根节点,再设置它的右孩子为刚刚切除的序列即可
- {
- Node *tmp;
- split(l,r,tmp);
- select(root->size-,null);//把自己手动设置的第n+1点移植到顶点,这样做的原因是防止他干扰,不移它的话,最右点就不是第n点,而是n+1点了
- select(root->size-,root);
- root->ch[]->ch[]=tmp;
- tmp->pre=root->ch[];
- root->ch[]->update();
- root->update();
- }
- void init(int num)
- {
- cur=nodePool;
- root=null;
- root=newNode(,null);
- root->ch[]=newNode(num+,root);//设置两个缓冲点,我刚刚还在犹豫n+1点是否需要,经实测确实需要,因为程序中随时要探测自己的子孩子,如果不设置,可能会溢出
- root->ch[]->ch[]=build(,num,root->ch[]);
- root->update();//
- }
- void output()
- {
- for (int i=;i<=n;i++)
- {
- select(i,null); //输出某点即把该点旋转到根节点再输出
- printf("%d\n",root->val);
- }
- }
- int main()
- {
- int a,b;
- while (scanf("%d%d",&n,&m)!=EOF)
- {
- init(n);
- //output();
- while (m--)
- {
- scanf("%d%d",&a,&b);
- if (a>b) swap(a,b);
- reverses(a,b);
- cut(a,b);
- }
- output();
- }
- }
UVA 11922 伸展树Splay 第一题的更多相关文章
- ZOJ 3765 Lights (zju March I)伸展树Splay
ZJU 三月月赛题,当时见这个题目没辙,没学过splay,敲了个链表TLE了,所以回来好好学了下Splay,这道题目是伸展树的第二题,对于伸展树的各项操作有了更多的理解,这题不同于上一题的用指针表示整 ...
- 树-伸展树(Splay Tree)
伸展树概念 伸展树(Splay Tree)是一种二叉排序树,它能在O(log n)内完成插入.查找和删除操作.它由Daniel Sleator和Robert Tarjan创造. (01) 伸展树属于二 ...
- 纸上谈兵: 伸展树 (splay tree)[转]
作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 我们讨论过,树的搜索效率与树的深度有关.二叉搜索树的深度可能为n,这种情况下,每 ...
- K:伸展树(splay tree)
伸展树(Splay Tree),也叫分裂树,是一种二叉排序树,它能在O(lgN)内完成插入.查找和删除操作.在伸展树上的一般操作都基于伸展操作:假设想要对一个二叉查找树执行一系列的查找操作,为了使 ...
- 高级搜索树-伸展树(Splay Tree)
目录 局部性 双层伸展 查找操作 插入操作 删除操作 性能分析 完整源码 与AVL树一样,伸展树(Splay Tree)也是平衡二叉搜索树的一致,伸展树无需时刻都严格保持整棵树的平衡,也不需要对基本的 ...
- UVA 11922 Permutation Transformer —— splay伸展树
题意:根据m条指令改变排列1 2 3 4 … n ,每条指令(a, b)表示取出第a~b个元素,反转后添加到排列尾部 分析:用一个可分裂合并的序列来表示整个序列,截取一段可以用两次分裂一次合并实现,粘 ...
- [Splay伸展树]splay树入门级教程
首先声明,本教程的对象是完全没有接触过splay的OIer,大牛请右上角.. 首先引入一下splay的概念,他的中文名是伸展树,意思差不多就是可以随意翻转的二叉树 PS:百度百科中伸展树读作:BoGa ...
- 伸展树Splay【非指针版】
·伸展树有以下基本操作(基于一道强大模板题:codevs维护队列): a[]读入的数组;id[]表示当前数组中的元素在树中节点的临时标号;fa[]当前节点的父节点的编号;c[][]类似于Trie,就是 ...
- POJ 3580 - SuperMemo - [伸展树splay]
题目链接:http://poj.org/problem?id=3580 Your friend, Jackson is invited to a TV show called SuperMemo in ...
随机推荐
- IDEA开发spring boot应用时 application.yml 或 application.properties 自定义属性提示
在使用spring boot开发过程中,经常会定义一些应用自己的属性,直接写到application配置文件中使用@Value注解进行使用,这样使用也没有什么问题.不过我认为更优雅的方式是定义自己的属 ...
- http error 502.5
原文地址:https://www.cnblogs.com/loui/p/7826073.html 在部署网站时遇到的各种问题,通过检索找到了解决方案,感谢!!记录一下以免忘记.. 解决方法:把IIS的 ...
- 吴裕雄 Bootstrap 前端框架开发——Bootstrap 字体图标(Glyphicons):glyphicon glyphicon-time
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name ...
- Linux学习《第四章脚本》20200222
- P1083 是否存在相等的差
P1083 是否存在相等的差 转跳点:
- netty权威指南学习笔记一——NIO入门(3)NIO
经过前面的铺垫,在这一节我们进入NIO编程,NIO弥补了原来同步阻塞IO的不足,他提供了高速的.面向块的I/O,NIO中加入的Buffer缓冲区,体现了与原I/O的一个重要区别.在面向流的I/O中,可 ...
- oracle 开发注意事项
新建表或字段时,不能使用char,统一使用varcha,防止判断null时有遗漏 新建表,索引,序列,新增删除或修改字段的时候,要先判断操作的对象是否存在,否则SLQ会报错 插入或者修改特殊字符,解决 ...
- Python 中使用 ddt 来进行数据驱动,批量执行用例,修改ddt代码
1. 什么是数据驱动? 使用数据驱动有什么好处? 用例执行是靠数据来驱动的,每条测试用例除了测试数据不一样意外,所有的用例代码都是一样的,为了使用例批量执行,我们会使用数据驱动的思想来批量执行测试用例 ...
- 关于springmvc的消息转换器
之前有用到消息转换器,一直是配置configureMessageConverters()这个方法的,虽然知道也有extendMessageConverters().它们的区别的是第一个不会继承框架默认 ...
- mybatis基础CURD的学习
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "- ...