splay反转-P3391 文艺平衡树
https://daniu.luogu.org/problem/show?pid=3391
首先你要理解splay的旋转;
其实反转和treap是一样的,都是二叉树的旋转;
但是treap用随机数来维护树高,而slplay用双旋来维护;
那我们怎么翻转这个区间呢?
对于l~r
我们先把l-1旋转到根节点
再把r+1旋转到更节点的右儿子;
显然,这样树根的右子节的左子节点点是l~r;
那么我们只要把这个区间打一个标记;
一旦我们要访问这个节点,先交换左右子树,再把标记下穿;
如果子树已经有标记就抵消;
这样就实现了翻转区间;
然后我们为l-1,r+1不越界,所以我们把整个区间的范围变化成0~n+2;
这样的话要注意一些细节问题;
有些心得说说;
在这个树里,树的权值(即节点)是一开始的下标;
但是我们维护的时这些下标的顺序;
因为顺序的变化,所以我们要把节点与节点之间的关系换掉;
但是节点本身不变;
这个就是和以前学的线段树什么的不一样的地方;
所以我们在写这类二叉搜索树的时候,一定要搞清楚。
我们维护的顺序是什么,节点的权值是什么;
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int c[100005][2],fa[100005],size[100005],rev[100005];//c数组记录左右儿子,rev就是标记;
int n,m,x,y,z,rt;
void pushup(int x){size[x]=size[c[x][0]]+size[c[x][1]]+1;}
void pushdown(int x){
if(!rev[x])return;
swap(c[x][0],c[x][1]);
rev[c[x][0]]^=1;
rev[c[x][1]]^=1;
rev[x]=0;
}
void make(int l,int r,int z){
if(l>r)return;
if(l==r){
fa[l]=z;
size[l]=1;
if(l<z)c[z][0]=l;else c[z][1]=l;
return;
}
int mid=l+r>>1;
make(l,mid-1,mid);
make(mid+1,r,mid);
fa[mid]=z;
pushup(mid);
if(mid<z)c[z][0]=mid;else c[z][1]=mid;
}
int find(int x,int k){
pushdown(x);
if(size[c[x][0]]+1==k)return x;
if(size[c[x][0]]+1>k)return find(c[x][0],k);
return find(c[x][1],k-size[c[x][0]]-1);
}
void turn(int x,int &k){
int y=fa[x],z=fa[y],l,r;
if(c[y][0]==x)l=0;else l=1;
r=l^1;//这里直接用l,r表示节点,这就是c[][0/1]的妙用,思想类似与滚存;
if(y==k)k=x;else
if(c[z][0]==y)c[z][0]=x;else c[z][1]=x;
fa[x]=z;fa[y]=x;fa[c[x][r]]=y;
c[y][l]=c[x][r];
c[x][r]=y;
pushup(y); pushup(x);
}
void splay(int x,int &k){
while(x!=k){//为什么这里x不用变,因为我们把x和其他点的关系改掉了,本身的x是不用边的;
//但是k最终会变成x;
int y=fa[x],z=fa[y];
if(y!=k)//如果y不是k,直接转两次,不然转一次;
if(c[y][0]==x^c[z][0]==y)turn(x,k);
else turn(y,k);
turn(x,k);
}
}
void rever(int l,int r){
int x=find(rt,l),y=find(rt,r);
splay(x,rt);
splay(y,c[rt][1]);
rev[c[y][0]]^=1;//用异或去添加,抵消标记
}
int main()
{
scanf("%d%d",&n,&m);
make(1,n+2,0);rt=(n+3)>>1;//建造一个0~n+2的树,节点个数是n+3个;
while(m--){
scanf("%d%d",&x,&y);
x++;y++;
rever(x-1,y+1);
}
for(int i=2;i<=n+1;i++)printf("%d ",find(rt,i)-1);
//因为我们一开始建立的0~n+2的区间,用了n+3个位子,第一个位子的下标是1,但是我们第一个数是0而不是1,所以-1;
}