• 【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('
    ');
    			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;
    }
    
  • 相关阅读:
    Ubuntu
    「日记」抑郁症
    [Ubuntu] 运行.AppImage格式文件
    [Database]Oracle数据库中concat和||的区别
    [Database] 不知道表名和字段查找值=1234的数据.
    [Windows]卸载Office 2016密钥
    [经验]怎么删除“通过QQ/TIM发送到”右键菜单
    Venom- Eminem
    粪便中的粪臭素稀释了以后会变成花香味
    #宽带选择# V2EX讨论
  • 原文地址:https://www.cnblogs.com/Sakura-TJH/p/YBT_JPDH_4-6-2.html
Copyright © 2020-2023  润新知