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

嗯,就是这样

原创粉丝点击