BZOJ 3223 浅谈SPLAY伸展树算法区间翻转
来源:互联网 发布:电脑游戏性能评测软件 编辑:程序博客网 时间:2024/06/05 03:59
世界真的很大
SPLAY是个很厉害的数据结构,相对于其他平衡树,比如treap,对于每一次查询虽然常数偏大,但是却能力保总复杂度趋近于nlogn,而且除了treap能支持的操作以外,还支持如区间翻转一类的特殊操作
其关键在于其独属的SPLAY操作
所以这次更多的是对SPLAY的一个详细总结
好像也是tarjan发明的,orz
看题先:
description:
您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 2 3 4 1
input
第一行为n,m n表示初始序列有n个数,这个序列依次是(1,2……n-1,n) m表示翻转操作次数接下来m行每行两个数[l,r] 数据保证 1<=l<=r<=n
output
输出一行n个数字,表示原始序列经过m次变换后的结果
题面还是比较良心的
看上去只需要一种操作,就是区间翻转,但是其他数据结构好像不是很好维护这个操作
首先谈谈SPLAY是一个什么东西
SPLAY首先是一颗BST,它有着BST共有的性质,即满足左小右大的性质,而旋转是BST几乎共有的一种操作,旨在不破坏BST性质的情况下调整树的结构
中序遍历是一种树的遍历方式,只要树是BST,那中序遍历的节点顺序就不会改变。因为中序遍历是左中右的顺序,而BST满足左小右大的性质,对于任意一个点,比他小的点在不修改的前提下是固定的,所以中序遍历的结果也不会改变
SPLAY的核心操作就是SPLAY,它的意思是将某一个节点通过旋转的方式,不破坏BST,到达指定位置。但指定位置一定得在它上面。这么做的目的是使得被多次访问的点尽可能的靠近根节点,这样在后续访问过程中使得访问尽量短,虽然每次访问会多了将访问节点旋转到根的时间,但却使后续对相同节点的访问时间缩短,从而使时间复杂度“平摊”,确保总操作复杂度趋于稳定。
而实现的方法其实也相对简单,就是旋转
如果目标节点就是当前节点的父亲,那直接旋转便是
否则采用双旋的办法,往上跳。
双旋右分2种
一种是当前节点的父亲,和当前节点父亲的父亲,也就是爷爷,三者形成了链式结构,就是说父亲是爷爷的左儿子,儿子也是父亲的做儿子,或者父亲是爷爷的右儿子,儿子也是父亲的右儿子,这样的,我们就先对父亲进行旋转,再旋转儿子
另一种是之字型结构,就是父亲是爷爷的左儿子,儿子是父亲的右儿子,或者父亲是爷爷的右儿子,儿子是父亲的左儿子,这样,就连续两下把儿子转上去
至于为什么不一样我也不是很懂,ZJC大神说链式结构连续单旋会被卡~~~~
再有就是其独特的区间翻转的性质。首先要实现区间翻转的话,就要明确是对什么建的一颗BST,并不是区间里的值,而是区间的下标。在转来转去的过程中,需要保证中序遍历一定是按区间下标12345。。。排好了的。
区间翻转饿第一步是找到需要翻转的区间,比如[L,R],这里的L也好,R也好,都是指区间的下标,而由于是对区间下标建的BST,所以要查下标对应的节点的位置是很容易的。而这里我们需要找到的是L-1和R+1的位置,通过SPLAY的方式将L-1节点转到根节点,R+1节点转到根节点的右子树,因为L-R下标一定比L-1大,这又是一颗BST,所以L-R区间一定在L-1,就是根节点的右边,同理一定在R+1,即根节点的右子树的左边,所以L-R对应的位置只能是且一定在根节点右儿子的左子树。
当然SPLAY只能把节点转到其祖先的位置,而如果L-1和R+1都在根节点的右子树或左子树怎么办哪
其实先SPLAY(L-1) 时,就已经使R+1在根节点的右边了。。。
这时L-1就是新的根了,而L,R就在根节点右子树的左子树。这时我们就使从根节点右儿子的左儿子的两个儿子开始,交换左右儿子,在逐层交换左右儿子
想想为什么这样是对的
假设总区间是1-10,要反转的区间是3-8,模拟一下SPLAY过程,则现在根节点是2,其右儿子是9,而右儿子的左儿子是5,而其子树就是3-8的区间了,5的左边是1-4,右边是6-8,交换左右儿子后就使得1-4到了右边,而6-8到了左边,是符合翻转定义的。
而考虑下一层,3的右边是4,而翻转后应该4在3的左边,交换左右子树。
考虑这样递归下去,类似于归并排序的思路,每个区间逐层翻转然后合并,而翻转前挨在一起的数翻转后必然还是挨在一起,所以是可以区间分治然后合并的
当然我们并不需要每次翻转就直接翻到底,不然成本太高,而是打一个标记,类似于线段树,在查询的时候在随着查询的深入而随便翻转
大概也就是这些了
完整代码:
#include<stdio.h>#include<algorithm>using namespace std;struct node{ int sum,flag,siz; node *ch[2],*fa; void pushdown(node *nd) { if(flag) { swap(ch[0],ch[1]); if(ch[0]!=nd) ch[0]->flag^=1; if(ch[1]!=nd) ch[1]->flag^=1; flag=0; } } void update() { siz=ch[0]->siz+ch[1]->siz+1; }}pool[4000010],*tail=pool,*root,*null;int n,m;void init(){ null=++tail; null->siz=0; null->ch[0]=null->ch[1]=null; null->sum=null->flag=0;}node* newnode(node *fa){ node *nd=++tail; nd->fa=fa; nd->siz=1; nd->ch[1]=nd->ch[0]=null; nd->flag=0; return nd;}void rot ( node*& x, int d ) { node* y=x->fa; y->ch[!d]=x->ch[d]; if(x->ch[d]!= null) x->ch[d]->fa=y ; x->fa=y->fa ; if(y->fa!=null) (y==y->fa->ch[0]) ? y->fa->ch[0]=x : y->fa->ch[1]=x; x->ch[d]=y; y->fa =x; x->update(); y->update();}node *build(node *fa,int lf,int rg){ if(lf>rg) return null; node *nd=newnode(fa); if(lf==rg) { nd->sum=lf; return nd; } int mid=(lf+rg)>>1; nd->sum=mid; nd->ch[0]=build(nd,lf,mid-1); nd->ch[1]=build(nd,mid+1,rg); nd->update(); return nd;}void Splay(node *nd,node *tar){ while(nd->fa!=tar) { node *ne=nd->fa; if(nd==ne->ch[0]) { if(ne->fa!=tar&&ne==ne->fa->ch[0]) rot(ne,1); rot(nd,1); } else { if(ne->fa!=tar&&ne==ne->fa->ch[1]) rot(ne,0); rot(nd,0); } } if(tar==null) root=nd;}node *kth(node *nd,int K){ nd->pushdown(null); if(nd->ch[0]->siz+1==K) return nd; if(nd->ch[0]->siz+1>K) return kth(nd->ch[0],K); else return kth(nd->ch[1],K-nd->ch[0]->siz-1);}void rev(int L,int R){ node *x=kth(root,L);node *y=kth(root,R+2); Splay(x,null); Splay(y,root); y->ch[0]->flag^=1;}void GrandOrder(node *nd){ if(nd==null) return ; nd->pushdown(null); GrandOrder(nd->ch[0]); if(nd->sum>=1&&nd->sum<=n) printf("%d ",nd->sum); GrandOrder(nd->ch[1]);}int main(){ init(); int L,R; scanf("%d%d",&n,&m); root=build(null,0,n+1); while(m--) { scanf("%d%d",&L,&R); rev(L,R); } GrandOrder(root); return 0;}
嗯,就是这样
- BZOJ 3223 浅谈SPLAY伸展树算法区间翻转
- BZOJ 3223 Splay区间翻转
- hdu 1890伸展树(splay tree)区间翻转
- hdu3487 (splay伸展树 区间翻转,切割,插入)
- 区间翻转 bzoj 3223 文艺平衡树 (splay)
- bzoj 1500 splay伸展树
- 伸展树(Splay tree)浅谈
- bzoj 3223: Tyvj 1729 文艺平衡树(splay 模板题 区间翻转)
- splay处理区间翻转例题【BZOJ 3223】 Tyvj 1729 文艺平衡树
- splay区间翻转bzoj 3223(tyvj 1729)文艺平衡树题解
- splay区间翻转(bzoj 3223: Tyvj 1729 文艺平衡树)
- 伸展树splay之求区间极值
- hdu 1890 伸展树区间翻转
- hdu1890 伸展树区间翻转复习
- 3303 翻转区间 伸展树的解法
- CODEVS 1743(伸展树区间翻转)
- [BZOJ]3223 文艺平衡树 区间翻转
- 【算法笔记】伸展树 Splay Tree
- 关于单例设计的一点小领悟
- C语言学习记录 引用传递
- 笔记
- 计算球的体积
- 水题--整数解 HDU 2092
- BZOJ 3223 浅谈SPLAY伸展树算法区间翻转
- Python——有1、2、3、4个数字,能组成多少个互不相同且无重复数字的三位数?都是多少?
- C语言(内存管理、文件处理)
- Java运算符
- Could not resolve placeholder
- main 主函数详解
- 关于getClass().getClassLoader() 为空的时候
- Mysql 视图
- dubbo控制台, jdk8以及linux的scp免密传输