FHQ 平衡树(二)
FHQ 平衡树
这次以文艺平衡树为例讲解FHQ 平衡树维护区间信息
前言
没有PHQ基础的先看这个
平衡树(一)
区间操作
反转区间
假设反转区间[l,r]
拆分成三段x[1,l-1],y[l,r],z[r+1,n],然后操作中间区间y后合并
对区间用懒标记即可,懒标记标记左右儿子是否反转,然后分裂合并前需要下穿标记
具体看代码注释
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=1e5+10;
int tot=0,root;
struct node{
int val,l,r,size,dat;bool rev;//rev为标记
}t[maxn];
int nw(int val){
t[++tot].val=val;
t[tot].dat=rand();
t[tot].size=1;
return tot;
}
void push_up(int p){
t[p].size=t[t[p].l].size+t[t[p].r].size+1;
}
void push_down(int now){//标记左右儿子
swap(t[now].l,t[now].r);//儿子两需要交换
t[t[now].l].rev^=1;
t[t[now].r].rev^=1;
t[now].rev=0;//取消标记
}
void split(int now,int size,int &x,int &y){
if(!now)x=y=0;
else{
if(t[now].rev)push_down(now);//分裂前就应把标记下传
if(t[t[now].l].size<size){
x=now;//属于分裂大小为size的树
split(t[now].r,size-t[t[now].l].size-1/*减去左子树大小和根,继续分裂的剩下大小*/,t[now].r,y);//往右
}
else {
y=now;
split(t[now].l,size,x,t[now].l);//往左
}push_up(now);
}
}
int merge(int x,int y){
if(!x||!y) return x+y;
if(t[x].dat>t[y].dat){
if(t[x].rev)push_down(x);
t[x].r=merge(t[x].r,y);
push_up(x);return x;
}
else{
if(t[y].rev)push_down(y);
t[y].l=merge(x,t[y].l);
push_up(y);return y;
}
}
void reverse(int l,int r){
int x,y,z;
//分裂成三个区间
split(root,l-1,x,y);
split(y,r-l+1,y,z);
//y为中间区间的根
t[y].rev^=1;//中间区间,打上标记
root=merge(merge(x,y),z);
}
void dfs(int now){//中序遍历输出
if(!now)return ;
if(t[now].rev) push_down(now);
dfs(t[now].l);printf("%d ",t[now].val);
dfs(t[now].r);
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i) root=merge(root,nw(i));//建树
while(m--){
int l,r;scanf("%d%d",&l,&r);
reverse(l,r);
}dfs(root); return 0;
}