# 【模板】文艺平衡树
## 题目描述
您需要写一种数据结构(可参考题目标题),来维护一个有序数列。
其中需要提供以下操作:翻转一个区间,例如原有序序列是 5 4 3 2 1,翻转区间是 [2,4] 的话,结果是 5 2 3 4 1。
## 输入格式
第一行两个正整数 n,m,表示序列长度与操作个数。序列中第 i 项初始为 i。
接下来 m 行,每行两个正整数 l,r,表示翻转的区间。
## 输出格式
输出一行 n个正整数,表示原始序列经过 m 次变换后的结果。
题意:维护区间信息,每次会选择一个区间,翻转这个区间,最后输出区间的信息
做法:splay,FHGtreap
FHGtreap做法
要翻转l~r区间,就是先打上标记,然后再下传懒标记,首先把整棵树按树的大小分裂成x(1~l - 1), z(l~n), 再将z树分裂成y(l~r), z(r + 1, n),这个时候得到的y树就是整个要操作的区间,最后别忘了把x, y, z树合并返回根节点
#include <iostream> #include <cstring> #include <algorithm> #include <unordered_set> #include <unordered_map> #include <set> #include <map> #include <cstdio> #include <vector> #include <queue> #include <cmath> using namespace std; #define x first #define y second typedef long long LL; typedef pair<int, int> PII; typedef pair<double, double> PDD; typedef pair<PII, int> PIII; typedef pair<LL, LL> PLL; const int N = 100010, M = 50000, mod = 998244353, INF = 0x3f3f3f3f, P = 1e8 - 3; struct tree{ int l, r; int val, key; int lazy, sz; }tr[N]; int root, cnt; int l, r; int newNode(int val) { tr[++ cnt].val = val; tr[cnt].key = rand(); tr[cnt].sz = 1; return cnt; } void update(int now) { tr[now].sz = tr[tr[now].l].sz + tr[tr[now].r].sz + 1; } void pushdown(int now) { swap(tr[now].l, tr[now].r); tr[tr[now].l].lazy ^= 1; tr[tr[now].r].lazy ^= 1; tr[now].lazy = 0; } int x, y, z; //按树的大小分裂 void spilt(int now, int sz, int &x, int &y) { if(!now) x = y = 0; else { if(tr[now].lazy) pushdown(now);//每次可能改变树的结构的操作前都要将懒标记下传 if(tr[tr[now].l].sz < sz) { x = now; spilt(tr[x].r, sz - tr[tr[now].l].sz - 1, tr[x].r, y); } else { y = now; spilt(tr[y].l, sz, x, tr[y].l); } update(now); } } int merge(int x, int y) { if(!x || !y) return x + y; else { if(tr[x].key > tr[y].key) { if(tr[x].lazy) pushdown(x); tr[x].r = merge(tr[x].r, y); update(x); return x; } else { if(tr[y].lazy) pushdown(y); tr[y].l = merge(x, tr[y].l); update(y); return y; } } } void ldr(int now) //输出中序遍历 { if(!now) return; if(tr[now].lazy) pushdown(now);//别忘了将剩下的懒标记下传 ldr(tr[now].l); printf("%d ", tr[now].val); ldr(tr[now].r); } void reverse(int l, int r) { spilt(root, l - 1, x, z); spilt(z, r - l + 1, y, z); tr[y].lazy ^= 1; merge(merge(x, y), z); } int main() { int n, m; scanf("%d %d", &n, &m); for(int i = 1; i <= n; i ++ ) root = merge(root, newNode(i)); while(m -- ) { scanf("%d %d", &l, &r); reverse(l, r); } ldr(root); return 0; }
splay:
把节点l转到根节点,再把r转到l的下面,这个时候节点r的左子树就是要操作的区间的子树,打上懒标记即可
#include <iostream> #include <cstring> #include <algorithm> using namespace std; const int N = 100010; struct Node{ int s[2], p, v; int sz, lazy; void init(int _v, int _p){ v = _v, p = _p; sz = 1; } }tr[N]; int root, idx; int n, m; void pushup(int u) { tr[u].sz = tr[tr[u].s[0]].sz + tr[tr[u].s[1]].sz + 1; } void pushdown(int u) { if(tr[u].lazy) { swap(tr[u].s[0], tr[u].s[1]); tr[tr[u].s[0]].lazy ^= 1; tr[tr[u].s[1]].lazy ^= 1; tr[u].lazy = 0; } } //左右旋 void rotate(int x) { int y = tr[x].p, z = tr[y].p; int k = tr[y].s[1] == x; tr[z].s[tr[z].s[1] == y] = x, tr[x].p = z; tr[y].s[k] = tr[x].s[k ^ 1], tr[tr[x].s[k ^ 1]].p = y; tr[x].s[k ^ 1] = y, tr[y].p = x; pushup(y), pushup(x); } void splay(int x, int k) { while(tr[x].p != k) { int y = tr[x].p, z = tr[y].p; if(z != k) if((tr[z].s[1] == y) ^ (tr[y].s[1] == x)) rotate(x); else rotate(y); rotate(x); } if(!k) root = x; } void insert(int v) { int u = root, p = 0; while(u) p = u, u = tr[u].s[v > tr[p].v]; u = ++ idx; if(p) tr[p].s[v > tr[p].v] = u; tr[u].init(v, p); splay(u, 0); } int get_k(int k) { int u = root; while(u) { pushdown(u); if(tr[tr[u].s[0]].sz >= k) u = tr[u].s[0]; else if(tr[tr[u].s[0]].sz + 1 == k) return u; else k -= tr[tr[u].s[0]].sz + 1, u = tr[u].s[1]; } return -1; } void ldr(int u) { if(!u) return; pushdown(u); ldr(tr[u].s[0]); if(tr[u].v >= 1 && tr[u].v <= n) printf("%d ", tr[u].v); ldr(tr[u].s[1]); } int main() { cin >> n >> m; for(int i = 0; i <= n + 1; i ++ ) insert(i); while(m -- ) { int l, r; cin >> l >> r; l = get_k(l), r = get_k(r + 2); splay(l, 0), splay(r, l); tr[tr[r].s[0]].lazy ^= 1; } ldr(root); return 0; }