题目:文艺平衡树
资料:
思路:
splay模板。
要加两个虚点1、n+2,原序列是 [2,n+1]。
一直在想为什么翻转不会破坏bst的性质,后来才发现这里的bst是对数的编号来说的,与值无关。
寻找一段区间,只需要把它的前驱旋转到根,后继旋转到根的右儿子,此时根的右儿子的左子树就是这段区间。
要用lazy tag优化。
代码:照着zcy大佬的敲的,加了点注释。
#include<bits/stdc++.h>
using namespace std;
#define maxn 100000
int n,m;
int rt;
int fa[maxn+5]; //父节点
int ch[maxn+5][3]; //子节点
int sz[maxn+5]; //以i为根的子树中的节点个数
int lzy[maxn+5]= {0}; //lazy tag
int p,q; //查询区间
void push_up(int o) { //通过子树的size计算父亲的size
sz[o]=sz[ch[o][0]]+sz[ch[o][1]]+1;
}
void push_down(int o) { //下传lazy标记
if(!lzy[o]) return ;
lzy[o]=0;
swap(ch[o][0],ch[o][1]); //交换左右子树
lzy[ch[o][0]]^=1,lzy[ch[o][1]]^=1; //下传
}
void make_tree(int o,int l,int r) { //建树
if(l>r) return ;
int mid=l+(r-l)/2;
fa[mid]=o,sz[mid]=1;
if(mid<o) ch[o][0]=mid;
else ch[o][1]=mid;
if(l==r) return ; //叶子结点
make_tree(mid,l,mid-1),make_tree(mid,mid+1,r);
push_up(mid);
}
int find(int x,int k) { //在以x为根的子树中查找第k个数的值
push_down(x);
int s=sz[ch[x][0]]; //左子树的size
if(k==s+1) return x; //k即为当前子树的根节点
if(k<=s) return find(ch[x][0],k); //k在左子树中
else return find(ch[x][1],k-s-1); //k在右子树中
}
void rotate(int x,int& k) { //旋转操作
int y=fa[x],z=fa[y]; //旋转边x-y
int t; //旋转后y在x的方位
if(ch[y][0]==x) t=1;
else t=0;
if(y==k) k=x; //y是根
else if(ch[z][0]==y) ch[z][0]=x; //让x成为z的子节点
else ch[z][1]=x;
fa[y]=x,fa[ch[x][t]]=y,fa[x]=z;
ch[y][t^1]=ch[x][t],ch[x][t]=y;
push_up(y),push_up(x); //上传size,注意不能写反
}
void splay(int x,int& k) { //将x旋转至k
while(x!=k) {
int y=fa[x],z=fa[y]; //x的上两层节点
if(y!=k) {
if((ch[y][0]==x)^(ch[z][0]==y)) rotate(x,k); //x,y,z在一条折线上
else rotate(y,k); //x,y,z在一条直线上
}
rotate(x,k);
}
}
void rev() {
int x=find(rt,p-1),y=find(rt,q+1); //当前查找区间的前驱和后继
splay(x,rt); //把前驱旋到根
splay(y,ch[x][1]); //把后继旋到根的右边
lzy[ch[y][0]]^=1; //根的右儿子的左子树就是所求区间
}
int main() {
scanf("%d%d",&n,&m);
rt=(n+3)/2;
make_tree(rt,1,n+2); //增加虚点1,n+2
for(int i=1; i<=m; i++) {
scanf("%d%d",&p,&q);
p++,q++;
rev();
}
for(int i=2; i<=n+1; i++) printf("%d ",find(rt,i)-1);
}