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;
}
posted @ 2021-08-19 17:58  归游  阅读(80)  评论(0编辑  收藏  举报