P3391 【模板】文艺平衡树

P3391 【模板】文艺平衡树

题目描述

您需要写一种数据结构(可参考题目标题),来维护一个有序数列。

其中需要提供以下操作:翻转一个区间,例如原有序序列是 5 4 3 2 1,翻转区间是 [2,4] 的话,结果是 5 2 3 4 1。

\(n,m\le 10^5\)

思路

注意此题平衡树维护的是编号,并不是数字本身。

平衡树Splay可以实现区间翻转功能,因为平衡树的中序遍历就是原数组,所以对一颗子树的所有左右儿子对调,就实现了对这一区间区间翻转。

所以我只需要让[l,r]在一颗子树里就行,方法是先将l-1这个节点Splay到根,再把r+1这个节点Splay到l-1的右儿子位置。此时r+1的左儿子就是所求子树。

然后就是懒标记,,,打上就行

代码

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<stack>
#include<vector>
#include<iomanip>

#define N 100010
#define INF 99999999
#define Ls (tr[id].s[0])
#define Rs (tr[id].s[1])
using namespace std;
int n,m,tot,root;
struct qwe{
	int s[2],fa;
	int val,siz;
	int tag;
}tr[N];

long long read(){
	long long x=0,h=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')h=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+(long long)(ch-'0');ch=getchar();}
	return x*h;
}

void pushdown(int id){
	if(tr[id].tag){
		tr[Ls].tag^=1;
		tr[Rs].tag^=1;
		// 旋转的核心 
		swap(Ls,Rs);
		tr[id].tag=0;
	}
	return ;
}

void print(int id){
	if(id==0)return ;
	pushdown(id);
	print(Ls);
//	printf("id:%d val:%d ls:%d rs:%d\n",id,tr[id].val,tr[id].s[0],tr[id].s[1]);
	if(tr[id].val!=INF&&tr[id].val!=-INF)printf("%d ",tr[id].val);
	print(Rs);
	return ;
}

void pushup(int id){
	// 这里记得+1,子树的根也得算进去 
	tr[id].siz=tr[Ls].siz+tr[Rs].siz+1;
	return ;
}

int typeson(int x){
	return x==tr[tr[x].fa].s[1];
}

void link(int f,int x,int tp){
	tr[f].s[tp]=x;
	tr[x].fa=f;
	return ;
}

void rotate(int x){
	int y=tr[x].fa,tp=typeson(x);
	link(y,tr[x].s[tp^1],tp);
	link(tr[y].fa,x,typeson(y));
	link(x,y,tp^1);
	pushup(y); pushup(x);
	return ;
}

void splay(int x,int gl){
	for(;tr[x].fa!=gl;){
		int f1=tr[x].fa,f2=tr[f1].fa;
		if(f2!=gl&&typeson(f1)==typeson(x))rotate(f1);
		rotate(x);
	}
	if(gl==0)root=x;
	return ;
}

int new_p(int x,int f){
	++tot;
	tr[tot].val=x; tr[tot].siz=1;
	tr[tot].fa=f;
	return tot;
}

void insert(int id,int x){
	if(root==0){
		root=new_p(x,0);
		return ;
	}
	int chd=(x>tr[id].val);
	if(!tr[id].s[chd]){
		tr[id].s[chd]=new_p(x,id);
		splay(tot,0);
	}
	else insert(tr[id].s[chd],x);
	pushup(id);
	return ;
}

int find(int id,int w){
	pushdown(id);
	if(w==tr[Ls].siz+1)return id;
	else {
		int k=(w>tr[Ls].siz+1);
		if(!k)return find(Ls,w);
		else return find(Rs,w-1-tr[Ls].siz);
	}
}

void reverse(int l,int r){
	l=find(root,l-1); r=find(root,r+1);
	splay(l,0); 
	splay(r,l);
	tr[tr[r].s[0]].tag^=1;
	return ;
}

int main(){
	n=read(); m=read();
	// 创建虚拟节点,防止把0号节点旋到根上 
	insert(root,-INF); insert(root,INF);
	// 这里也可以使用build建树,建树速度更快 
	for(int i=1;i<=n;i++)insert(root,i);
	for(int i=1;i<=m;i++){
		// 因为加入了虚拟节点,所以下标右移 
		int l=read()+1,r=read()+1;
		reverse(l,r);
	}
	print(root);
	return 0;
}

posted @ 2022-03-23 18:29  Charisk_FOD  阅读(39)  评论(0)    收藏  举报