您的位置:首页 > 其它

bzoj 3223 文艺平衡树 Splay详细解析

2017-07-14 20:51 309 查看
本文不会过于深入介绍splay的证明,感性认知- -。

对于树我们知道它有两个旋转,对于一个节点,如果它是左儿子,那么它只能右旋,如果它是右儿子,它只能左旋,在splay的时候,我们的目的是把一个节点x转到一个根上,我们考虑两种情况,

1在三点一线的时候(3个点没有弯曲)那么先旋转x的父亲,在旋转x,除此之外都直接旋转x就好了,这样是为了让树更平衡。(画个图感性认知一下0 0)

splay大概流程

void rotate(int x,int &k){
int y=t[x].fa,z=t[y].fa,p;
if(t[y].ch[0]==x)p=1;else p=0;
if(y==k)k=x;
else {if(t[z].ch[0]==y)t[z].ch[0]=x;else t[z].ch[1]=x;}
t[x].fa=z;t[y].fa=x;t[y].ch[p^1]=t[x].ch[p];
t[t[x].ch[p]].fa=y;t[x].ch[p]=y;
update(x);update(y);
}
void splay(int x,int &k){
while(x!=k){
int y=t[x].fa,z=t[y].fa;
if(y!=k){
if(t[y].ch[0]==x^t[z].ch[0]==y)rotate(x,k);
else rotate(y,k);
}
rotate(x,k);
}
}


然后我们来分析这道题

对于本题来说,我们需要翻转区间。首先我们考虑在数组上,每一个位置对应一个值,如果翻转区间[ L, R ] 就是把所有大于l-1 并且小于r+1 的位置翻转,那么我们很自然的想到bst的性质,然后想到平衡树(根据题目名称可知)。

然后把翻转转到树上就是交换节点的左右子树,然后递归下去交换。

接下来就比较绕了,为什么交换左右子树能保持bst性质呢?如何找到树上某个点呢?

因为我们要找的是在数组上的位置,所以应该是第几个点,而不是第几号点,所以只有在最开始的时候节点编号才恰好为第几个点。那么其实在每次交换子树的时候,我们其实就是在原数组中该位置的点左边部分和右边部分给交换了位置。所以能解释它为什么两边siz都变了还能满足bst性质。

更可以这么理解的是,每次交换后,都相当于改变了一次每个点是第几号点。

例如1,2,3,4,5我们交换了2到4,变为1,4,3,2,5我们可以发现4它从第4个点变成了第2个点。自动就改变了,是不是很神奇O(∩_∩)O哈哈~

下面是我A掉的代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100005
using namespace std;
int n,m,rt;
struct date{
int ch[2],fa,lazy,siz;
date(){ch[0]=-1;ch[1]=-1;}
};
date t
;
void update(int x){
int l=t[x].ch[0],r=t[x].ch[1];
t[x].siz=t[l].siz+t[r].siz+1;
}
void pushdown(int k){
if(t[k].lazy){
int &l=t[k].ch[0],&r=t[k].ch[1];
swap(l,r);
t[l].lazy^=1,t[r].lazy^=1,t[k].lazy^=1;
}
}
void rotate(int x,int &k){ int y=t[x].fa,z=t[y].fa,p; if(t[y].ch[0]==x)p=1;else p=0; if(y==k)k=x; else {if(t[z].ch[0]==y)t[z].ch[0]=x;else t[z].ch[1]=x;} t[x].fa=z;t[y].fa=x;t[y].ch[p^1]=t[x].ch[p]; t[t[x].ch[p]].fa=y;t[x].ch[p]=y; update(x);update(y); } void splay(int x,int &k){ while(x!=k){ int y=t[x].fa,z=t[y].fa; if(y!=k){ if(t[y].ch[0]==x^t[z].ch[0]==y)rotate(x,k); else rotate(y,k); } rotate(x,k); } }
int build(int l,int r,int f){
if(l>r)return -1;
if(l==r){
t[l].fa=f,t[l].siz=1,t[l].lazy=0;
return l;
}
else{
int mid=(l+r)>>1;
t[mid].ch[0]=build(l,mid-1,mid);
t[mid].ch[1]=build(mid+1,r,mid);
t[mid].fa=f,t[mid].lazy=0;
update(mid);
return mid;
}
}
int find(int k,int val){
pushdown(k);
int l=t[k].ch[0],r=t[k].ch[1];
if(t[l].siz+1==val)return k;
else if(t[l].siz>=val)return find(l,val);
else return find(r,val-t[l].siz-1);
}
void rev(int l,int r){
int x=find(rt,l),y=find(rt,r+2);
splay(x,rt);splay(y,t[x].ch[1]);
int z=t[y].ch[0];
t[z].lazy^=1;
}
int main(){
int l,r;
scanf("%d%d",&n,&m);
rt=build(0,n+1,-1);
for(int i=1;i<=m;i++){
scanf("%d%d",&l,&r);
rev(l,r);
}
for(int i=1;i<=n;i++)
printf("%d ",find(rt,i+1));
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  splay