正题
题目连接:https://www.luogu.org/problemnew/show/P3391
题目大意
一个序列,m个操作翻转[l..r][l..r][l..r]区间。求最终序列
解题思路
节点维护编号,然后答案就是中序遍历。然后翻转的话我们先考虑一个性质。
若这是初始状态(l-1和r+1反了)
然后将l-1旋到根节点,将r+1选到根的右节点,
然后l∼rl\sim rl∼r就是一个单独的子树,可以自己翻转。
但这里不翻转先,因为最终只有一次询问,所以我们可以用延迟标记。
codecodecode
#include<cstdio>
#include<algorithm>
#define INF 2100000000
#define N 100010
using namespace std;
struct splay{int v[N],father[N],root;int l[N],r[N];int sum[N],mark[N];int n,points;void Updata(int x){sum[x]=sum[l[x]]+sum[r[x]]+1;}void Downdata(int x){if(mark[x]){mark[l[x]]^=1;mark[r[x]]^=1;mark[x]=0;swap(l[x],r[x]);}}void New(int x,int vs,int fa){l[x]=r[x]=0;sum[x]=1;v[x]=vs;father[x]=fa;}void Rotate(int x){int y=father[x];int z=father[y];int k=r[y]==x;if(r[z]==y) r[z]=x;else l[z]=x;father[x]=z;if(k) r[y]=l[x];else l[y]=r[x];father[k?l[x]:r[x]]=y;if(k) l[x]=y;else r[x]=y;father[y]=x;Updata(y);Updata(x);}void Splay(int at,int to){while(father[at]!=to){int y=father[at];int z=father[y];if(z!=to)(r[z]==y)^(r[z]==at)?Rotate(at):Rotate(y);Rotate(at);}if(to==0)root=at;}void Insert(int x){int now=root,ff=0;while(now)ff=now,now=(x>v[now]?r[now]:l[now]);now=++n;if(ff&&x>v[now]) r[ff]=now;else if(ff) l[ff]=now;New(now,x,ff);Splay(now,0);}int GetValByRank(int k){int now=root;while(1){Downdata(now);if(sum[l[now]]>=k) now=l[now];else if(sum[l[now]]+1==k) return now;else k-=sum[l[now]]+1,now=r[now];}}void Work(int ls,int rs){ls=GetValByRank(ls);rs=GetValByRank(rs+2);Splay(ls,0);Splay(rs,ls);mark[l[r[root]]]^=1;}void Write(int x,int mn){Downdata(x);if(l[x]) Write(l[x],mn);if(v[x]>1&&v[x]<mn+2) printf("%d ",v[x]-1);if(r[x]) Write(r[x],mn);}
}a;
int main(){//freopen("testdata.in","r",stdin);//freopen("data.out","w",stdout);int n,m;scanf("%d%d",&n,&m);for(int i=1;i<=n+2;i++)a.Insert(i);while(m--){int l,r;scanf("%d%d",&l,&r);a.Work(l,r);}a.Write(a.root,n);
}