普通平衡树(按权值划分)
#include <iostream>
#include <random>//rand()最大32767
using namespace std;
mt19937 dice(random_device{}());
const int N = 1e5 + 7;
//rd[i]-第i个结点的随机数,sz[i]-以i为根的子树中结点的个数
int son[N][2], val[N], rd[N], sz[N], root;
int new_node(int v) {
static int ct;
sz[++ct] = 1, val[ct] = v, rd[ct] = dice();
return ct;
}
void update(int x) {//更新以x为根节点的整棵树的元素个数
sz[x] = 1 + sz[ son[x][0] ] + sz[ son[x][1] ];
}
//合并以x和y为根的两棵子树,x必须在y左边,可以是父结点或左儿子
int merge(int x, int y) {
//x==0则返回y,y==0则返回x
if (!x || !y) return x + y;
if (rd[x] < rd[y]) {//维护小根堆的性质不变
son[x][1] = merge(son[x][1], y);
update(x);
return x;
} else {
son[y][0] = merge(x, son[y][0]);
update(y);
return y;
}
}
//按结点权值分裂,将小于等于v的所有结点分裂为一棵以x为根结点的树
//其余结点分给以y为根结点的树
void split(int rt, int v, int &x, int &y) {
if (!rt) { x = y = 0; return; }
if (val[rt] <= v)
x = rt, split(son[x][1], v, son[x][1], y);
else
y = rt, split(son[y][0], v, x, son[y][0]);
update(rt);
}
void insert(int v) {
int x, y;
//将小于等于v的结点split出来,以x作为根,剩余元素以y为根
split(root, v, x, y);//x和y的参数作为引用参数,在split中被赋值
//合并时第一个参数的树中的值都小于等于第二个参数的树中的值
root = merge(merge(x, new_node(v)), y);
}
void rm(int v) {
//我们把小于v的和大于v的部分都单独分裂出来,把中间等于v的去掉一个
int x, y, z;
//将大于v的结点分给z,将小于v的结点分给x,等于v的就是y
split(root, v, x, z), split(x, v-1, x, y);
//直接将y的左儿子和右儿子merge,就去掉了y自己
y = merge(son[y][0], son[y][1]);
root = merge(merge(x, y), z);
}
void rank_v(int v) {
int x, y;
split(root, v-1, x, y);
printf("%d\n", sz[x] + 1);
root = merge(x, y);
}
void kth(int rt, int k) {
while (k-1 != sz[ son[rt][0] ]) {//根结点不是第k个元素
if (k <= sz[ son[rt][0] ])
rt = son[rt][0];
else
k -= sz[ son[rt][0] ] + 1, rt = son[rt][1];
}
printf("%d\n", val[rt]);
}
void pre(int v) {
int x, y;
split(root, v-1, x, y);
kth(x, sz[x]);
root = merge(x, y);
}
void post(int v) {
int x, y;
split(root, v, x, y);
kth(y, 1);
root = merge(x, y);
}
int main() {
int n;
scanf("%d", &n);
while (n--) {
int op, v;
scanf("%d%d", &op, &v);
switch(op) {
case 1: insert(v); break;
case 2: rm(v); break;
case 3: rank_v(v); break;
case 4: kth(root, v); break;
case 5: pre(v); break;
case 6: post(v); break;
}
}
return 0;
}
文艺平衡树(按rank划分)
#include <iostream>
#include <random>
using namespace std;
mt19937 dice(random_device{}());
const int N = 1e5 + 7;
int son[N][2], root, val[N], size[N], rk[N], n;
bool tag[N];
int new_node(int v) {
static int sz;
val[++sz] = v, size[sz] = 1, rk[sz] = dice();
return sz;
}
void update(int u) {
size[u] = 1 + size[ son[u][0] ] + size[ son[u][1] ];
}
int build(int lo, int hi) {//递归的build都是按先序进行创建
if (lo > hi) return 0;
int mid = (lo+hi)>>1, v = mid-1;
int cur = new_node(v);
son[cur][0] = build(lo, mid-1);
son[cur][1] = build(mid+1, hi);
update(cur);
return cur;
}
void pushdown(int x) {//懒标记,一次只下传一层
if (!x || !tag[x]) return;
tag[x] = 0;
std::swap(son[x][0], son[x][1]);
if (son[x][0]) tag[ son[x][0] ] ^= 1;
if (son[x][1]) tag[ son[x][1] ] ^= 1;
}
int merge(int x, int y) {
if (!x || !y) return x + y;
pushdown(x), pushdown(y);
if (rk[x] < rk[y]) {
son[x][1] = merge(son[x][1], y);
update(x);
return x;
}
son[y][0] = merge(x, son[y][0]);
update(y);
return y;
}
void split(int cur, int k, int &x, int &y) {//算上哨兵,应该找k+1
if (!cur) { x = y = 0; return; }
pushdown(cur);//下传懒标记
if (k <= size[ son[cur][0] ])//cur排名在前k个以外
y = cur, split(son[y][0], k, x, son[y][0]);
else //cur排名在前k个以内
x = cur, split(son[x][1], k-1-size[ son[x][0] ], son[x][1], y);
update(cur);
}
void rot(int lo, int hi) {
int u, v, s, t;
split(root, hi+1, u, v);
split(u, lo, s, t);
tag[t] ^= 1;
root = merge(merge(s, t), v);
}
void dfs(int u) {//中序遍历,带懒标记的
if (!u) return;
pushdown(u);
dfs(son[u][0]);
if (val[u]>=1 && val[u]<=n)
printf("%d ", val[u]);
dfs(son[u][1]);
}
int main() {
int m, l, r;
scanf("%d%d", &n, &m);
root = build(1, n+2);
while (m--) {
scanf("%d%d", &l, &r);
rot(l, r);
}
dfs(root);
return 0;
}