可持久化平衡树
题目链接:ybt金牌导航4-6-2 / luogu P3835
题目大意
要你支持一些操作。
插入数,删除数,查询数排名,查询某个排名的数,查询数的前驱后继。
但是它要求可以可持久化,即每次会在给定的历史版本上改动。
思路
其实可持续化平衡树和平衡树很像。
我们只用类比一下线段树和主席树,就不难想到要怎么搞了。
由于我们旋转就不好搞持久化,我们可以用没有旋转。
那当然是用无旋 Treap 啦。
大概的方法跟主席树差不多,就是拆树和合并数复制点来存值。
然后由于你之前的树不会被真的拆掉,当我们只是为了查询的时候我们可以不用拆掉再合并回去,我们只用让这次版本值你改动版本的根节点一样就行了。(反正你只是查询,树还是一模一样的)
代码
#include<queue>
#include<cstdio>
#include<cstdlib>
using namespace std;
int n, rt[500001], bb, op, x, tot;
int ls[500001 << 6], rs[500001 << 6], val[500001 << 6], sz[500001 << 6], yj[500001 << 6];
int read() {
int re = 0, zf = 1;
char c = getchar();
while (c < '0' || c > '9') {
if (c == '-') zf = -zf;
c = getchar();
}
while (c >= '0' && c <= '9') {
re = (re << 3) + (re << 1) + c - '0';
c = getchar();
}
return re * zf;
}
void write(int now) {
if (now < 0) putchar('-'), now = -now;
if (now > 9) write(now / 10);
putchar(now % 10 + '0');
}
void up(int now) {
sz[now] = sz[ls[now]] + sz[rs[now]] + 1;
}
int newpoint(int num) {
int re = ++tot;
ls[re] = rs[re] = 0;
val[re] = num;
sz[re] = 1;
yj[re] = rand();
return re;
}
int copypoint(int pl) {//复制之前的点
int re = ++tot;
ls[re] = ls[pl];
rs[re] = rs[pl];
val[re] = val[pl];
sz[re] = sz[pl];
yj[re] = yj[pl];
return re;
}
pair <int, int> split_val(int now, int num) {
if (!now) return make_pair(0, 0);
pair <int, int> re;
if (num < val[now]) {
int noww = copypoint(now);//记得是可持续化,要复制点(下面也一样)
re = split_val(ls[noww], num);
ls[noww] = re.second;
up(noww);
re.second = noww;
}
else {
int noww = copypoint(now);
re = split_val(rs[noww], num);
rs[noww] = re.first;
up(noww);
re.first = noww;
}
return re;
}
int merge(int x, int y) {
if (!x) return y;
if (!y) return x;
if (yj[x] < yj[y]) {
int xx = copypoint(x);
rs[xx] = merge(rs[xx], y);
up(xx);
return xx;
}
else {
int yy = copypoint(y);
ls[yy] = merge(x, ls[yy]);
up(yy);
return yy;
}
}
void insert(int bb, int num) {
pair <int, int> x = split_val(rt[bb], num);
int y = newpoint(num);
rt[bb] = merge(merge(x.first, y), x.second);
}
void delete_(int bb, int num) {
pair <int, int> x = split_val(rt[bb], num);
pair <int, int> y = split_val(x.first, num - 1);
y.second = merge(ls[y.second], rs[y.second]);
rt[bb] = merge(merge(y.first, y.second), x.second);
}
//由于你可持续化不会把原来的真的割开,所以你不用重新合并,而是直接跟之前的根一样就可以(下面也一样)
int ask_rnk(int bb, int num) {
pair <int, int> x = split_val(rt[bb], num - 1);
int re = sz[x.first] + 1;
return re;
}
int ask_val(int now, int rnk) {
while (now) {
if (sz[ls[now]] >= rnk) now = ls[now];
else if (sz[ls[now]] + 1 == rnk) return val[now];
else {
rnk -= sz[ls[now]] + 1;
now = rs[now];
}
}
}
int get_pre(int bb, int num) {
pair <int, int> x = split_val(rt[bb], num - 1);
if (!x.first) return -2147483647;
return ask_val(x.first, sz[x.first]);
}
int get_nxt(int bb, int num) {
pair <int, int> x = split_val(rt[bb], num);
if (!x.second) return 2147483647;
return ask_val(x.second, 1);
}
int main() {
srand(19491001);
n = read();
for (int i = 1; i <= n; i++) {
bb = read(); op = read(); x = read();
rt[i] = rt[bb];
if (op == 1) {
insert(i, x);
continue;
}
if (op == 2) {
delete_(i, x);
continue;
}
if (op == 3) {
write(ask_rnk(i, x));
putchar('
');
continue;
}
if (op == 4) {
write(ask_val(rt[i], x));
putchar('
');
continue;
}
if (op == 5) {
write(get_pre(i, x));
putchar('
');
continue;
}
if (op == 6) {
write(get_nxt(i, x));
putchar('
');
continue;
}
}
return 0;
}