Link:
Solution:
$Splay$对序列操作的模板题
核心思想就是将$L-1$移到根,$R+1$移至$L-1$的下方,从而约束出区间$[L,R]$进行操作
对于此题仅要求将区间翻转,因此只要将左右子树交换即可
但同时类似于线段树要打上懒惰标记来保证复杂度,每次查询第$K$大时下放标记
Tips:
1、对于此题不再关心每个数的权值,而只关心其在序列中的编号
而这个节点的排名($sz[ch[x][0]]+1$)就是其当前的序号
2、为了处理$[1,n]$等边界情况,新增$1,n+2$作为真·边界
3、一开始可以通过分治直接建树,不用一个个$insert$,但要将$f[rt]$置为$0$!!!
Code:
#include <bits/stdc++.h> using namespace std; const int MAXN=1e5+10; int n,m,rt,l,r,sz[MAXN],f[MAXN],ch[MAXN][2],tag[MAXN]; void pushup(int x) {sz[x]=sz[ch[x][0]]+sz[ch[x][1]]+1;} void pushdown(int x) { if(!tag[x]) return; tag[ch[x][1]]^=1;tag[ch[x][0]]^=1; swap(ch[x][0],ch[x][1]);tag[x]=0; } void Build(int l,int r,int anc) { if(l>r) return; int mid=(l+r)/2; if(mid<anc) ch[anc][0]=mid; else ch[anc][1]=mid; sz[mid]=1;f[mid]=anc; if(l==r) return; Build(l,mid-1,mid);Build(mid+1,r,mid); pushup(mid); } void Print(int x) { pushdown(x); if(ch[x][0]) Print(ch[x][0]); if(x>1&&x<n+2) printf("%d ",x-1); if(ch[x][1]) Print(ch[x][1]); } void Rotate(int x) { int y=f[x],z=f[y],k=(ch[y][1]==x); ch[z][ch[z][1]==y]=x;f[x]=z; ch[y][k]=ch[x][k^1];f[ch[x][k^1]]=y; ch[x][k^1]=y;f[y]=x; pushup(x);pushup(y); } void Splay(int x,int up) { while(f[x]!=up) { int y=f[x],z=f[y]; if(z!=up) (ch[y][1]==x)^(ch[z][1]==y)?Rotate(x):Rotate(y); Rotate(x); } if(!up) rt=x; } int Kth(int x) { int k=rt; while(true) { pushdown(k); if(x==sz[ch[k][0]]+1) return k; else if(sz[ch[k][0]]>=x) k=ch[k][0]; else x-=sz[ch[k][0]]+1,k=ch[k][1]; } } void Reverse(int l,int r) { int x=Kth(l),y=Kth(r+2); Splay(x,0);Splay(y,x); tag[ch[ch[rt][1]][0]]^=1; } int main() { scanf("%d%d",&n,&m); rt=(n+3)/2;Build(1,n+2,rt);f[rt]=0; for(int i=1;i<=m;i++) scanf("%d%d",&l,&r),Reverse(l,r); Print(rt); return 0; }