网址:https://www.luogu.org/problem/P3391
题意:
给出一个数列,$a_i=i$,维护数列的区间翻转,输出所有翻转后的结果。
题解:
一、treap解法:
涉及区间翻转的$treap$解法只能使用无旋$treap$,将无旋$treap$分裂成三棵子树,分别是区间左,区间和区间右,然后给区间打上标记然后交换子树,在$merge$的时候下传标记即可,由于$merge$的时候其他子树上的标记没有下传,所以$split$的时候也要先下传标记再$split$。平衡树的中序遍历输出的一定是原序列,所以最后我们只要中序遍历,输出的就是结果了。
AC代码:
#include
using namespace std;
const int MAXN = 1e5 + 5;
struct Treap
{
int val[MAXN], son[MAXN][2], rnk[MAXN], size[MAXN];
bool tag[MAXN];
int sz;
void init()
{
sz = 0;
}
void pushup(int x)
{
size[x] = size[son[x][0]] + size[son[x][1]] + 1;
}
void pushdown(int x)
{
if (tag[x])
{
tag[x] = 0;
tag[son[x][0]] ^= 1;
tag[son[x][1]] ^= 1;
swap(son[x][0], son[x][1]);
}
}
int _rand()
{
static int seed = 12345;
return seed = int(seed * 482711ll % 2147483647);
}
void merge(int& rt, int a, int b)
{
if (!a || !b)
{
rt = a + b;
return;
}
if (rnk[a] < rnk[b])
{
pushdown(a), rt = a;
merge(son[rt][1], son[a][1], b);
}
else
{
pushdown(b), rt = b;
merge(son[rt][0], a, son[b][0]);
}
pushup(rt);
}
void split_sz(int rt, int& a, int& b, int s)
{
if (rt == 0)
{
a = b = 0;
return;
}
pushdown(rt);
if (size[son[rt][0]] < s)
{
a = rt;
split_sz(son[rt][1], son[a][1], b, s - size[son[a][0]] - 1);
}
else
{
b = rt;
split_sz(son[rt][0], a, son[b][0], s);
}
pushup(rt);
}
void split_v(int rt, int& a, int& b, int v)
{
if (rt == 0)
{
a = b = 0;
return;
}
pushdown(rt);
if (val[a] <= v)
a = rt, split_v(son[rt][1], son[a][1], b, v);
else
b = rt, split_v(son[rt][0], a, son[b][0], v);
pushup(rt);
}
int newnode(int x)
{
int rt = ++sz;
val[rt] = x;
rnk[rt] = _rand();
son[rt][0] = son[rt][1] = 0;
size[rt] = 1;
tag[rt] = 0;
return rt;
}
void insert(int& rt, int v)
{
int x = 0, y = 0, n = newnode(v);
split_v(rt, x, y, v);
merge(x, x, n);
merge(rt, x, y);
}
void rerverse(int& rt, int l, int r)
{
int x = 0, y = 0, z = 0;
split_sz(rt, x, y, r);
split_sz(x, x, z, l - 1);
tag[z] ^= 1;
merge(x, x, z);
merge(rt, x, y);
}
void print(int rt)
{
pushdown(rt);
if (son[rt][0])
print(son[rt][0]);
printf("%d ", val[rt]);
if (son[rt][1])
print(son[rt][1]);
}
};
Treap tr;
int p = 0;
int main()
{
tr.init();
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i)
tr.insert(p, i);
int l, r;
for (int i = 1; i <= m; ++i)
{
scanf("%d%d", &l, &r);
tr.rerverse(p, l, r);
}
tr.print(p);
putchar('\n');
return 0;
}
二、splay解法:
$splay$的解法就是先把左端点的前驱$splay$到根,右端点的后驱$splay$到根的右儿子,显然根的左儿子就是需要翻转的区间,打标记即可,然后splay的时候先下传标记再翻转就可以了,由于$splay$旋转时旋转点上必须要有儿子,所以要加上无穷小和无穷大防止$splay$死循环,输出方法同$treap$。
AC代码:
// luogu-judger-enable-o2
#include
using namespace std;
const int MAXN = 1e5 + 5;
struct Splay
{
int size[MAXN], val[MAXN], fa[MAXN];
int num[MAXN], son[MAXN][2], sta[MAXN];
bool tag[MAXN];
int sz, rt;
void init()
{
rt = sz = 0;
}
void up(int x)
{
if (x)
{
size[x] = num[x];
if (son[x][0])
size[x] += size[son[x][0]];
if (son[x][1])
size[x] += size[son[x][1]];
}
}
void down(int x)
{
if (tag[x])
{
tag[x] = 0;
tag[son[x][0]] ^= 1;
tag[son[x][1]] ^= 1;
swap(son[x][0], son[x][1]);
}
}
void con(int x, int y, int z)
{
if (x)
fa[x] = y;
if (y)
son[y][z] = x;
}
int getson(int x)
{
return son[fa[x]][1] == x;
}
void rotate(int x)
{
int fx = fa[x], ffx = fa[fx];
int fs = getson(x), ffs = getson(fx);
con(son[x][fs ^ 1], fx, fs);
con(fx, x, fs ^ 1);
con(x, ffx, ffs);
up(fx), up(x);
}
void splay(int x, int end)
{
end = fa[end];
int top = 0;
for (int f = x; f; f = fa[f])
sta[++top] = f;
for (int i = top; i; --i)
down(sta[i]);
int f;
while (fa[x] != end)
{
f = fa[x];
if (fa[f] != end)
rotate(getson(x) == getson(f) ? f : x);
rotate(x);
}
if (!end)
rt = x;
}
int newnode(int x, int f)
{
int root = ++sz;
val[root] = x;
size[root] = num[root] = 1;
son[root][0] = son[root][1] = 0;
fa[root] = f;
son[f][x > val[f]] = root;
tag[root] = 0;
return root;
}
void insert(int x)
{
if (!rt)
{
rt = newnode(x, 0);
return;
}
int now = rt, f = 0;
while (1)
{
if (x == val[now])
{
++num[now];
up(now), up(f);
splay(now, rt);
return;
}
f = now, now = son[now][x > val[now]];
if (!now)
{
int tmp = newnode(x, f);
up(f);
splay(tmp, rt);
return;
}
}
}
int querynum(int rnk)
{
int ans = 0, now = rt;
while (1)
{
down(now);
if (son[now][0] && rnk <= size[son[now][0]])
{
now = son[now][0];
continue;
}
if (son[now][0])
rnk -= size[son[now][0]];
if (rnk <= num[now])
{
splay(now, rt);
return now;
}
rnk -= num[now];
now = son[now][1];
}
}
void reverse(int ql, int qr)
{
int l = querynum(ql), r = querynum(qr + 2);
splay(l, rt), splay(r, son[l][1]);
tag[son[r][0]] ^= 1;
}
void print(int x,int n)
{
down(x);
if (son[x][0])
print(son[x][0], n);
if (val[x] > 1 && val[x] < n + 2)
printf("%d ", val[x] - 1);
if (son[x][1])
print(son[x][1], n);
}
};
Splay sp;
int main()
{
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n + 2; ++i)
sp.insert(i);
int l, r;
for (int i = 0; i < m; ++i)
{
scanf("%d%d", &l, &r);
sp.reverse(l, r);
}
sp.print(sp.rt, n);
printf("\n");
return 0;
}