前情回顾:蒟蒻的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);
}