蒟蒻的splay2之文艺平衡树

Posted lcez56jsy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了蒟蒻的splay2之文艺平衡树相关的知识,希望对你有一定的参考价值。

前情回顾:蒟蒻的splay1
时隔好久终于更新了.jpg
文艺平衡树
简单来说就是要求区间翻转
首先我们按照点的编号来建一颗二叉搜索树,建树方式类似线段树(注意空间要开大不然会(T)
这样建出来的树类似这样:
技术图片
然后我们就可以在树上找到区间了
但是我们怎么翻转呢?
如果这个区间长度为2,那么我们可以暴力的(swap)
如果长度更长呢?我们是不是可以按某种规则去(swap)?
注意到如果将(l-1)(splay)到根,(r+1)(splay)到根的右儿子,则(r+1)的左子树就是我们要翻转的区间。然后翻转这个子树就可以了。
如果暴力(swap)整颗子树,肯定会(T),而且某个点可能被翻转多次(即有可能最终位置不变),所以我们用标记来搞某个点是否翻转。这样翻转子树的时候只用搞搞标记再(swap)一下就(ok)
来康康代码叭

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
inline int read()
{
	char ch=getchar();
	int x=0;bool f=0;
	while(ch>‘9‘||ch<‘0‘)
	{
		if(ch==‘-‘)f=1;
		ch=getchar();
	}
	while(ch>=‘0‘&&ch<=‘9‘)
	{
		x=(x<<3)+(x<<1)+(ch^48);
		ch=getchar();
	}
	return f?-x:x; 
}
const int inf=214748364;
int n,m,sz;
int key[1000100],val[100010],root,size[1000100],ch[1000100][2],tag[1000100],par[1001000];
bool get(int x)
{
	return ch[par[x]][1]==x;
}
void pushup(int x)
{
	size[x]=size[ch[x][0]]+size[ch[x][1]]+1;
}
void pushdown(int x)//下放标记
{
	if(!x)return ;
	if(!tag[x])return;
	tag[ch[x][0]]^=1;
	tag[ch[x][1]]^=1;
	swap(ch[x][0],ch[x][1]);
	tag[x]=0;
	return ;
}
int build(int fa,int l,int r)//建树
{
	if(l>r) return 0;
	sz++;
	int now=sz;
	int mid=(l+r)>>1;
	key[now]=val[mid];
	par[now]=fa;
	ch[now][0]=build(now,l,mid-1);
	ch[now][1]=build(now,mid+1,r);
	pushup(now);
	return now;
}
void rotate(int x)
{
	int y=par[x],z=par[y],k=get(x),e=get(y);
	pushdown(y);pushdown(x);
	ch[y][k]=ch[x][k^1];
	par[ch[y][k]]=y;
	ch[x][k^1]=y;par[y]=x;
	par[x]=z;
	if(z)
	 ch[z][e]=x;
	pushup(y);pushup(x);
}
void splay(int x,int goal)
{
	for(int fa;(fa=par[x])!=goal;rotate(x))
	{
		if(par[fa]!=goal)
		  rotate((get(x)==get(fa))?fa:x);
	}
	if(!goal)root=x;
}
int rnk(int x)//查询某个点的排名 
{
	int now=root;
	while(1)
	{
		pushdown(now);
		if(x<=size[ch[now][0]])now=ch[now][0];
		else
		{
			x-=size[ch[now][0]]+1;
			if(!x)
			{
			return now;
			}
			now=ch[now][1];
		}
	}
}
void print(int now)//最终输出
{
        pushdown(now);
	if(ch[now][0])print(ch[now][0]);
	if(key[now]!=inf&&key[now]!=-inf)printf("%d ",key[now]);
	if(ch[now][1])print(ch[now][1]);
}
void fz(int l,int r)
{
	l=rnk(l);//因为有虚点,所以l是真正的l-1
	r=rnk(r+2);//r+2同理
	splay(l,0);
	splay(r,l);
	pushdown(root);
	tag[ch[ch[root][1]][0]]^=1;//标记根的右儿子的左子树要翻转

}

int main()
{
	n=read();m=read();
	for(int i=1;i<=n;i++)
	 val[i+1]=i;
	val[1]=-inf;val[n+2]=inf;//插入两个虚点防RE
	root=build(0,1,n+2);
	for(int i=1;i<=m;i++)
	{
		int l=read(),r=read();
                fz(l,r);
	}
	print(root);
}

以上是关于蒟蒻的splay2之文艺平衡树的主要内容,如果未能解决你的问题,请参考以下文章

hihocoder #1333 : 平衡树·Splay2

题解 P3369 模板普通平衡树(Treap/SBT)

[bzoj4636]蒟蒻的数列_线段树

BZOJ_4636_蒟蒻的数列_线段树+动态开点

fhq-Treap 文艺平衡树代码记录

文艺平衡树(splay模板)