传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3223
题解:Splay区间翻转,用一个数组rev作标记,如果rev=1,那么就将其左右儿子交换,并将rev向其左右儿子传递,这样就可以实现区间翻转。
Code
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 100010
using namespace std;
int n,m,root;
int ls[N],rs[N],s[N],v[N],f[N],rev[N];
void PushUp(int k)
{
s[k]=s[ls[k]]+s[rs[k]]+1;
}
void PushDown(int k)
{
if(!rev[k])return ;
swap(ls[k],rs[k]);
rev[ls[k]]^=1,rev[rs[k]]^=1;
rev[k]=0;
}
void zig(int x)
{
int y=f[x],z=f[y];
if(ls[z]==y)ls[z]=x;
else rs[z]=x;
f[x]=z,f[y]=x,ls[y]=rs[x],f[rs[x]]=y,rs[x]=y;
PushUp(y),PushUp(x);
if(y==root)root=x;
}
void zag(int x)
{
int y=f[x],z=f[y];
if(ls[z]==y)ls[z]=x;
else rs[z]=x;
f[x]=z,f[y]=x,rs[y]=ls[x],f[ls[x]]=y,ls[x]=y;
PushUp(y),PushUp(x);
if(y==root)root=x;
}
void Splay(int x,int k)
{
while(f[x]!=k)
{
int y=f[x];
if(ls[y]==x)zig(x);
else zag(x);
}
}
int find(int &k,int x)
{
PushDown(k);
if(s[ls[k]]+1==x)return k;
if(s[ls[k]]>=x)return find(ls[k],x);
return find(rs[k],x-s[ls[k]]-1);
}
void build(int l,int r,int fa)
{
if(l>r)return ;
int now=v[l],last=v[fa];
if(l==r)
{
s[now]=1;
f[now]=last;
if(l<fa)ls[last]=now;
else rs[last]=now;
return ;
}
int mid=(l+r)>>1;
now=v[mid];
build(l,mid-1,mid);
build(mid+1,r,mid);
f[now]=last;
if(mid<fa)ls[last]=now;
else rs[last]=now;
PushUp(mid);
}
void rever(int l,int r)
{
int x=find(root,l);
int y=find(root,r);
Splay(x,0);
Splay(y,x);
rev[ls[y]]^=1;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n+2;i++)v[i]=i;
root=(n+3)>>1;
build(1,n+2,0);
for(int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
rever(x,y+2);
}
for(int i=2;i<=n+1;i++)printf("%d ",find(root,i)-1);
return 0;
}