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