LibreOJ105 - 文艺平衡树 (平衡树)
Description
这是一道模板题。
您需要写一种数据结构(可参考题目标题),来维护一个序列,其中需要提供以下操作:
翻转一个区间,例如原有序序列是 5 4 3 2 1,翻转区间是 [2,4] 的话,结果是 5 2 3 4 1。
思路
这题体现了无旋treap的区间操作。
注意不能按照关键值来排序,要按照当前结点的位置来排序。更新过程中维护好当前结点之前有多少结点(cnt)。
这样treap就可以把区间分裂出来。分裂后再合并,会改变treap的结构,却不改变treap的中序遍历。
所以如果有一个有序序列,将它们插入到treap中,分裂的treap的中序遍历就是这个有序序列某个子序列。所以可以把这个treap看成这个子序列,对其进行各种操作。
翻转区间就是把区间[l,r]的treap分裂出来,然后交换每个结点的左右子结点。
为什么可以这样做呢?因为中序遍历的顺序是:左结点,此结点,右结点。这样就可以按顺序(比如从小到大,看你结点关系怎么组织)输出。
如果你swap子树的每个结点的左右结点,相当于以右结点,此结点,左结点的顺序来遍历,结果就是顺序反过来(逆序),相当于翻转。可以使用懒惰标记(类似线段树),来减少不必要的翻转。
而且交换每个结点的左右子结点不会改变堆的性质,使得翻转后还是个合法的treap。
#include <iostream>
#include <cstdio>
#include <queue>
#include <algorithm>
#include <map>
#include <set>
#include <vector>
#include <cstring>
#include <string>
#include <stack>
#include <deque>
#include <cmath>
#include <iomanip>
#include <cctype>
#define endl '\n'
#define IOS std::ios::sync_with_stdio(0);
#define FILE freopen("..//data_generator//in.txt","r",stdin),freopen("res.txt","w",stdout)
#define FI freopen("..//data_generator//in.txt","r",stdin)
#define FO freopen("res.txt","w",stdout)
#define pb cpush_back
#define mp make_pair
#define seteps(N) fixed << setprecision(N)
typedef long long ll;
using namespace std;
/*-----------------------------------------------------------------*/
#define INF 0x3f3f3f3f
const int N = 1e6 + 10;
const int M = 1e9 + 7;
const double eps = 1e-8;
int pos[N];
int lc[N], rc[N];
int val[N];
int cnt[N];
int lazy[N];
int si;
typedef pair<int, int> PII;
void pushdown(int rt) {
if(lazy[rt]) {
swap(lc[rt], rc[rt]);
lazy[lc[rt]]++;
lazy[rc[rt]]++;
lazy[lc[rt]] %= 2;
lazy[rc[rt]] %= 2;
lazy[rt] = 0;
}
}
PII split(int rt, int key) {
if(!key || !rt) {
return mp(0, rt);
}
pushdown(rt);
if(key < cnt[lc[rt]] + 1) {
PII o = split(lc[rt], key);
lc[rt] = o.second;
cnt[rt] = cnt[lc[rt]] + cnt[rc[rt]] + 1;
return mp(o.first, rt);
} else {
PII o = split(rc[rt], key - cnt[lc[rt]] - 1);
rc[rt] = o.first;
cnt[rt] = cnt[lc[rt]] + cnt[rc[rt]] + 1;
return mp(rt, o.second);
}
}
int merge(int lrt, int rrt) {
if(!lrt) return rrt;
if(!rrt) return lrt;
pushdown(lrt);
pushdown(rrt);
if(pos[lrt] > pos[rrt]) {
rc[lrt] = merge(rc[lrt], rrt);
cnt[lrt] = cnt[lc[lrt]] + cnt[rc[lrt]] + 1;
return lrt;
} else {
lc[rrt] = merge(lrt, lc[rrt]);
cnt[rrt] = cnt[lc[rrt]] + cnt[rc[rrt]] + 1;
return rrt;
}
}
int insert(int v, int rt) {
++si;
val[si] = v;
pos[si] = rand();
cnt[si] = 1;
return merge(rt, si);
}
void print(int rt) {
if(!rt) return ;
pushdown(rt);
print(lc[rt]);
cout << val[rt] << " ";
print(rc[rt]);
}
int reverse(int l, int r, int rt) {
int tar;
PII o1 = split(rt, r);
tar = o1.first;
PII o2 = o1;
if(l > 1) {
o2 = split(tar, l - 1);
tar = o2.second;
}
lazy[tar] = 1;
if(l > 1) return merge(merge(o2.first, o2.second), o1.second);
return merge(o1.first, o1.second);
}
int main() {
IOS;
//FO;
int n, m;
cin >> n >> m;
int rt = 0;
for(int i = 1; i <= n; i++) {
rt = insert(i, rt);
}
while(m--) {
int l, r;
cin >> l >> r;
rt = reverse(l, r, rt);
}
print(rt);
}