有一个(1,2,3...n)的排列,给出m次操作,每次操作翻转区间[l,r],并且把翻转后的区间移动到排列末端,输出最后的排列。
splay的区间翻转操作,第一次写。
开始的时候插入两个虚拟节点0和n+1,这样原本的操作区间[l-1,r+1]就变成了[l,r+2];采用类似线段树的lazy标记,表示该子树表示的区间是否翻转;向下更新的时候下传懒标记,交换子树的编号即可。注意一下输出的时候需要下传懒标记。
#include<cstdio>
#include<iostream>
#define MAXN 100010
using namespace std;
int p,q,n,m,tot,root,a[MAXN],ch[MAXN][2],sz[MAXN],fa[MAXN],rev[MAXN],v[MAXN];
void pushdown(int x)
{
if(rev[x])
{
rev[ch[x][0]] ^= 1;
rev[ch[x][1]] ^= 1;
swap(ch[x][0],ch[x][1]);
rev[x] = 0;
}
}
void pushup(int x)
{
sz[x] = sz[ch[x][0]]+sz[ch[x][1]]+1;
}
void rotate(int x)
{
int y = fa[x],z = fa[y],f = (ch[y][1]==x);
ch[y][f] = ch[x][!f];
fa[ch[y][f]] = y;
ch[x][!f] = y,fa[y] = x;
fa[x] = z;
ch[z][ch[z][1]==y] = x;
pushup(y);
pushup(x);
}
void splay(int x,int goal)
{
for(int y; (y=fa[x]) != goal; rotate(x))
{
int z = fa[y];
if(z != goal)
{
if((ch[z][0] == y) == (ch[y][0] == x)) rotate(y);
else rotate(x);
}
}
if(!goal) root = x;
pushup(x);
}
void rotateto(int k,int goal)
{
int x = root;
while(1)
{
pushdown(x);
if(k < sz[ch[x][0]]+1) x = ch[x][0];
else if(k > sz[ch[x][0]]+1) k -= sz[ch[x][0]]+1,x = ch[x][1];
else break;
}
splay(x,goal);
}
void New(int &x,int val)
{
x = ++tot;
v[x] = val;
sz[x] = 1;
}
void build_tree(int &x,int L,int R,int F)
{
int mid = (L+R)/2;
New(x,a[mid]);
fa[x] = F;
if(L == R) return;
if(L < mid) build_tree(ch[x][0],L,mid-1,x);
if(R > mid) build_tree(ch[x][1],mid+1,R,x);
pushup(x);
}
void reverse_and_move(int L,int R)
{
rotateto(L,0);
rotateto(R+2,root);
int u = ch[ch[root][1]][0];
rev[u] ^= 1;
ch[ch[root][1]][0] = 0;
pushup(ch[root][1]);
pushup(root);
rotateto(n+1-sz[u],0);//第一个数其实是节点数!!!
rotateto(n+2-sz[u],root);
ch[ch[root][1]][0] = u;
fa[u] = ch[root][1];
pushup(ch[root][1]);
pushup(root);
}
void Print(int x)
{
if(x == 0) return;
pushdown(x);
Print(ch[x][0]);
if(v[x] != 0)
printf("%d\n",v[x]);
Print(ch[x][1]);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i = 1; i <= n; i++) a[i] = i;
build_tree(root,0,n+1,0);
for(int i = 1; i <= m; i++)
{
scanf("%d%d",&p,&q);
reverse_and_move(p,q);
}
Print(root);
}