【ybt金牌导航4-6-2】【luogu P3835】可持久化平衡树

可持久化平衡树

题目链接: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('\n');
			continue;
		}
		if (op == 4) {
			write(ask_val(rt[i], x));
			putchar('\n');
			continue;
		}
		if (op == 5) {
			write(get_pre(i, x));
			putchar('\n');
			continue;
		}
		if (op == 6) {
			write(get_nxt(i, x));
			putchar('\n');
			continue;
		}
	}
	
	return 0;
}
posted @ 2021-05-26 10:15  あおいSakura  阅读(31)  评论(0编辑  收藏  举报