splay模板

本文介绍了一种使用C++实现的文艺平衡树(非标准二叉搜索树),重点讲解了如何插入节点、查找节点以及通过旋转操作进行区间反转,以解决题目P3391中的问题。通过递归和迭代方法处理节点旋转和区间调整,确保了树的平衡性。
摘要由CSDN通过智能技术生成

洛谷P3391文艺平衡树

#include<iostream>
#include<cstdio>
#include<cstdlib>
using namespace std;
const int N=1e6,inf=0x3f3f3f3f;
struct node
{
	bool rev;//rev记录该节点的子树是否需要反转 
	//val既为节点权值,也为初始时节点在区间中的下标
	//(但因为之后会交换左右子树,所以val的大小不一定满足BST性质,因此只有建树时才有用)
	//(所以后面的查找只能用排名来体现一个节点的大小关系(因为是1~n的排列,排名即数值)) 
	//size为该节点子树大小 
	int val,size; 
	int ch[2],fa;//ch[0]为左儿子,ch[1]为右儿子,fa为父亲
}tree[N];
int n,m,tot,rt;
int id(int k){return tree[tree[k].fa].ch[1]==k;}//得到当前节点是父亲的左还是右儿子
void update(int k)
{
	if(k)//如果k不为空(一定要判这个,不然0号节点就。。。)
	{
		tree[k].size=1;
		if(tree[k].ch[0]) tree[k].size+=tree[tree[k].ch[0]].size;//同上 
		if(tree[k].ch[1]) tree[k].size+=tree[tree[k].ch[1]].size;//同上 
	}
}
void pushdown(int k)//在需要调用k的子节点时下传反转标记,因为k的子节点可能已经被反转 
{
	if(k&&tree[k].rev)//如果k不为空(只要有可能访问到空节点的操作都要判空)
	{
		tree[tree[k].ch[0]].rev^=1;
		tree[tree[k].ch[1]].rev^=1;
		swap(tree[k].ch[0],tree[k].ch[1]);//交换k的左右子树,相当于反转一个区间 
		tree[k].rev=0;
	}
}
void rotate(int k)
{
	int fa=tree[k].fa,gfa=tree[fa].fa;
	pushdown(k),pushdown(fa);//fa,k都要调用自己的子节点的值,下传标记 
	//gfa虽然也要调用子节点,但其只调用了子节点的方向,不会导致答案的值出错 
	bool d=id(k);
	tree[tree[k].fa=gfa].ch[id(fa)]=k;//连接k与fa的fa 
	tree[tree[fa].ch[d]=tree[k].ch[!d]].fa=fa;//连接k的儿子与fa 
	tree[tree[k].ch[!d]=fa].fa=k;//连接k与fa 
	update(fa);//由于k还要不断向上走,值会不断变化,只需更新留下来的fa即可 
}
void splay(int k,int top)
{
	for(int fa;(fa=tree[k].fa)!=top;rotate(k))//如果k已经在top下,退出循环;
	//否则无论是下面哪种情况,k都要旋一次 
	//if为false:k的爷爷就是top,k旋一次即可
	//if为true:
	//{
	//	k,fa,fa的fa在同一方向上:先旋fa,再旋k 
	//	k,fa,fa的fa在不同方向上:k旋两次 
	//} 
	if(tree[fa].fa!=top)
	rotate((id(k)==id(fa)?fa:k));
	update(k);//最后更新k的值 
	if(top==0) rt=k;//如果k是根,更新根的值 
}
void insert(int &k,int val,int fa)
{
	if(k==0)
	{
		k=++tot;
		tree[k].size=1,tree[k].val=val;
		tree[k].fa=fa;
		splay(k,0);//将k旋到根上,保证复杂度 
		return;
	}
	int d=val<tree[k].val?0:1;
	insert(tree[k].ch[d],val,k);
	update(k);
}
//查找只能用排名来体现一个节点的大小关系(因为是1~n的排列,排名即数值)
int find(int k,int x)
{
	pushdown(k);
	if(tree[tree[k].ch[0]].size+1==x) return k;//如果当前节点排名就为x 
	if(x<=tree[tree[k].ch[0]].size)//排名为x的点在左子树中 
	return find(tree[k].ch[0],x);
	if(tree[tree[k].ch[0]].size+1<x)//排名为x的点在右子树中,x减去当前节点及左子树大小 
	return find(tree[k].ch[1],x-tree[tree[k].ch[0]].size-1);
}
void reverse(int l,int r)
{
	int x=find(rt,l),y=find(rt,r+2);//找到为l-1和r+1的点    ,因为插入了-inf,所以+1
	//将x旋到根上,将y旋到x下 
	//因为y大于x,所以y肯定是x的右儿子,而权值为l~r的节点一定在y的左子树上 
	splay(x,0),splay(y,x);
	tree[tree[y].ch[0]].rev^=1;
}
void dfs(int k)
{
	if(k==0) return;
	pushdown(k);
	//二叉查找树的中序遍历为原序列 
	dfs(tree[k].ch[0]);
	if(tree[k].val!=-inf&&tree[k].val!=inf)
	printf("%d ",tree[k].val);
	dfs(tree[k].ch[1]);
}
int main()
{
	scanf("%d%d",&n,&m);
	insert(rt,-inf,tree[rt].fa),insert(rt,inf,tree[rt].fa);
	//插入正负无穷,防止反转区间1~n时爆炸 
	for(int i=1;i<=n;i++)
	insert(rt,i,tree[rt].fa);
	for(int i=1,l,r;i<=m;i++)
	{
		scanf("%d%d",&l,&r);
		reverse(l,r);
	}
	dfs(rt);
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值