题意
给定n个数,对这个数列进行m次操作,每次将一个选定区间[l, r]翻转。求出m次操作后得到的数列。
前置知识
splay
分析
这应该算是splay的经典应用了吧。
翻转本身不算难事:对于一个带翻转的区间[l, r],先将l-1旋转至树根,再将r+1旋转至根的有节点。这样一来,[l,r]的节点全部都在r+1的左儿子处了。
于是,我们给每个节点一个tag,表示这棵子树下的区间是否需要翻转。访问到这个节点时,将标记下放,并交换左右子树就行了。
看到这里,你或许会想:交换左右子树?这不就把排序二叉树的特性给破坏了吗?
所以,我们并不将值a[i]塞到节点里,而是直接用值最开始时的下标i来作为节点的值。接下来讲一下原理:
在之前敲splay的时候,我们一般像这样储存splay:
struct node{
int ch[2];//0-左儿子,1-右儿子
int siz;//子树大小
int sum;//当前值出现的次数
int val;//节点的值
int fa;//父亲编号
}t[max_n];
我们为了讨论方便,将它称作“树表”。
回想一下splay操作:
inline void rorate(int x)
{
int y = t[x].fa, z = t[y].fa, son = (t[y].ch[1] == x);
t[z].ch[t[z].ch[1] == y] = x, t[z].fa = x;
t[y].ch[son] = t[x].ch[!son], t[t[x].ch[!son]].fa = y;
t[x].ch[!son] = y, t[y].fa = x;
}
inline void splay(int x, int to)
{
while(t[x].fa != to)
{
int y = t[x].fa, z = t[y].fa;
if(z != to)
((t[z].ch[1] == y) == (t[y].ch[1] == x)) ? rorate(y) : rorate(x);
}
if(!to)
root = to; //root表示根
}
我们发现:不管我们如何对splay进行操作,只是树的层次关系发生变化,但这个节点在原树表的绝对位置始终不变。
所以,我们可以这样构建一颗完美的树:把原序列的中点拎起来,将序列分成两部分,然后对于这两部分递归处理即可。
这样一来,左右节点的含义发生了改变:左子树表示在现在的数列中在mid左边的元素,右子树的含义同理,交换子树时,就相当于将[l,mid-1]与[mid+1,r]交换位置,这样就完成了一次区间翻转。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int mn = 100005, inf = 1 << 30;
struct node{
int ch[2], siz, fa;
bool tag;
}t[mn];
int a[mn], root;
bool flag;
inline void update(int s)
{
t[s].siz = t[t[s].ch[0]].siz + t[t[s].ch[1]].siz + 1;
}
inline void push_down(int s)//懒标记下传
{
int lch = t[s].ch[0], rch = t[s].ch[1];
if(t[s].tag)
t[s].tag = 0, t[lch].tag = !t[lch].tag, t[rch].tag = !t[rch].tag, swap(t[s].ch[0], t[s].ch[1]);
}
int make_tree(int l, int r, int f)
{
if(l > r)
return 0;
int mid = (l + r) >> 1;
t[mid].fa = f, t[mid].ch[0] = make_tree(l, mid - 1, mid), t[mid].ch[1] = make_tree(mid + 1, r, mid), update(mid);
return mid;
}
inline void rorate(int x)
{
int y = t[x].fa, z = t[y].fa, son = (t[y].ch[1] == x);
t[z].ch[t[z].ch[1] == y] = x, t[x].fa = z;
t[y].ch[son] = t[x].ch[!son], t[t[x].ch[!son]].fa = y;
t[x].ch[!son] = y, t[y].fa = x;
update(y), update(x);
}
inline void splay(int x, int to)
{
while(t[x].fa != to)
{
int y = t[x].fa, z = t[y].fa;
if(z != to)
((t[z].ch[1] == y) == (t[y].ch[1] == x)) ? rorate(y) : rorate(x);
rorate(x);
}
if(!to)
root = x;
}
inline int get_num(int x)
{
int s = root;
while(1)
{
push_down(s);
int lch = t[s].ch[0];
if(t[lch].siz + 1 < x)
x -= t[lch].siz + 1, s = t[s].ch[1];
else if(t[lch].siz >= x)
s = lch;
else
return s;
}
}
inline void rev(int l, int r)
{
int pre = get_num(l - 1), nxt = get_num(r + 1);
splay(pre, 0), splay(nxt, pre);
t[t[nxt].ch[0]].tag = !t[t[nxt].ch[0]].tag;
}
void print(int s)
{
if(!s)
return;
push_down(s);
print(t[s].ch[0]);
if(a[s] != inf && a[s] != -inf)
{
if(flag)
putchar(' ');
flag = 1, printf("%d", s - 1);
}
print(t[s].ch[1]);
}
int main()
{
int n, m, i, l, r;
scanf("%d%d", &n, &m);
for(i = 2; i <= n + 1; i++)
a[i] = i - 1;
a[1] = -inf, a[n + 2] = inf, root = make_tree(1, n + 2, 0);//哨兵节点,避免找不到前驱后继
while(m--)
{
scanf("%d%d", &l, &r), ++l, ++r;
rev(l, r);
}
print(root);
}