网址:https://www.luogu.org/problem/P3391
题意:
给出一个数列,$a_i=i$,维护数列的区间翻转,输出所有翻转后的结果。
题解:
一、treap解法:
涉及区间翻转的$treap$解法只能使用无旋$treap$,将无旋$treap$分裂成三棵子树,分别是区间左,区间和区间右,然后给区间打上标记然后交换子树,在$merge$的时候下传标记即可,由于$merge$的时候其他子树上的标记没有下传,所以$split$的时候也要先下传标记再$split$。平衡树的中序遍历输出的一定是原序列,所以最后我们只要中序遍历,输出的就是结果了。
AC代码:
#includeusing 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 #includeusing 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; }