• 洛谷 P3369 【模板】普通平衡树


    写平衡树真的是要自闭……一个多小时终于写完+调完了(或许我是一区(62)级信息组里最晚会(treap)的人了……)

    发现写(treap)的题解比较少……于是自己看着黄学长的代码写了一篇,注释写的很明白,都在代码里了,不过要注意的是在做这道题之前一定要先学会二叉搜索树和堆,否则就会很难理解,这两个东西都不算难,可以自己去网上搜一下,这里就不贴链接了

    下面就看代码吧(qwq)

    #include <cstdio>
    #include <cstring>
    #include <cstdlib>
    #include <iostream>
    using namespace std;
    
    const int A = 1e5 + 11;
    
    inline int read() {
    	char c = getchar(); int x = 0, f = 1;
    	for( ; !isdigit(c); c = getchar()) if(c == '-') f = -1; 
    	for( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
    	return x * f;
    }
    
    int n, size, root, ans;
    struct data { int l, r, v, size, rnd, w; } tr[A];
    //l左儿子,r右儿子,v权值,size子树大小,rnd随机数,w为当前权值的个数 
    
    void update(int rt) {
    	tr[rt].size = tr[tr[rt].l].size + tr[tr[rt].r].size + tr[rt].w;
    	//当前子树的大小等于左子树大+右子树大小+当前节点权值的个数 
    }
    
    //右旋 
    /*
    当前节点的左儿子变成右旋节点的右儿子
    旋转节点的右儿子变成当前节点
    旋转节点的子树大小变为当前节点的子树大小
    更新当前节点子树大小 
    */
    void rturn(int &rt) {
    	int t = tr[rt].l;
    	tr[rt].l = tr[t].r;
    	tr[t].r = rt;
    	tr[t].size = tr[rt].size;
    	update(rt); rt = t;
    }
    
    //左旋
    /*
    当前节点的右儿子变成左旋节点的左儿子
    旋转节点的左儿子变成当前节点
    旋转节点的子树大小变为当前节点的子树大小
    更新当前节点子树大小
    */ 
    void lturn(int &rt) {
    	int t = tr[rt].r;
    	tr[rt].r = tr[t].l;
    	tr[t].l = rt; 
    	tr[t].size = tr[rt].size;
    	update(rt); rt = t; 
    }
    
    //插入节点
    void insert(int &rt, int x) {
    	if(rt == 0) {//没有这种权值的节点就新建一个 
    		rt = ++size;
    		tr[rt].size = tr[rt].w = 1;//因为是新建的,所以siz和w都为1
    		tr[rt].v = x, tr[rt].rnd = rand(); return;
    	}
    	tr[rt].size++;//如果目前节点编号不为0则让当前节点的size++ 
    	if(tr[rt].v == x) { tr[rt].w++; return; } 
    	//如果找到了相同权值的节点就直接让当前节点相同值的个数++并直接返回
    	if(x > tr[rt].v) {//如果大于当前节点的权值就到右子树里寻找(二叉搜索树性质) 
    		insert(tr[rt].r, x);
    		if(tr[tr[rt].r].rnd < tr[rt].rnd) lturn(rt);//维护堆性质  
    	}
    	else {//否则去左儿子 
    		insert(tr[rt].l, x);
    		if(tr[tr[rt].l].rnd > tr[rt].rnd) rturn(rt);//维护堆性质 
    	}
    } 
    
    //删除节点 
    void del(int &rt, int x) {
    	if(rt == 0) return; //如果rt是0说明没有找到,直接返回
    	if(tr[rt].v == x) { //找到啦~~ 
    		if(tr[rt].w > 1) { //如果不止一个只删除一个,相应的size也要-- 
    			tr[rt].size--, tr[rt].w--; return;
    		}
    		if(tr[rt].l * tr[rt].r == 0) rt = tr[rt].l + tr[rt].r; //有一个儿子为空 
    		else if(tr[tr[rt].l].rnd < tr[tr[rt].r].rnd) rturn(rt), del(rt, x);
    		else lturn(rt), del(rt, x);
    	}
    	else if(x > tr[rt].v) tr[rt].size--, del(tr[rt].r, x);
    	else tr[rt].size--, del(tr[rt].l, x);
    } 
    
    //x的排名 
    int rank(int rt, int x) {
    	if(rt == 0) return 0;//没有就返回0
    	if(tr[rt].v == x) return tr[tr[rt].l].size + 1;
    	//当前节点找到了,答案就是左子树大小+1,因为左子树中节点的权值都比当前节点小,右子树都比当前大
    	if(x > tr[rt].v) return tr[tr[rt].l].size + tr[rt].w + rank(tr[rt].r, x);
    	//如果当前节点权值小于x,则到右子树中寻找x,此时需要返回左儿子大小加上当前节点权值个数再加上右子树中x的排名 
    	return rank(tr[rt].l, x); 
    }
    
    //排名为x的数
    int num(int rt, int x) {
    	if(rt == 0) return 0;//老套路
    	if(x <= tr[tr[rt].l].size) return num(tr[rt].l, x);
    	//因为二叉搜索树的性质,所以左子树的权值都比当前小,如果x小于左子树的size,就到左子树中去找
    	if(x > tr[tr[rt].l].size + tr[rt].w) return num(tr[rt].r, x - tr[tr[rt].l].size - tr[rt].w);
    	//如果x大于左子树大小与当前权值个数之和的大小 就到右子树中去找,找的时候要让x减去左子树大小和当前权值个数之和
    	//即到右子树中寻找第x - tr[tr[rt].l].size - tr[rt].w大的数 
    	return tr[rt].v; //负责就是找到了,直接return 
    }
    
    //这里的ans是指节点编号,最后还要输出节点的值 
    //查询前驱precursor(就是指小于当前值的最大值)
    void pre(int rt, int x) {
    	if(rt == 0) return;//老套路 * 2
    	if(tr[rt].v < x) ans = rt, pre(tr[rt].r, x);
    	else pre(tr[rt].l, x);
    }
    
    //查询后继successor(就是指大于当前值的最小值) 
    void suc(int rt, int x) {
    	if(rt == 0) return;//老套路 * 3
    	if(tr[rt].v > x) ans = rt, suc(tr[rt].l, x);
    	else suc(tr[rt].r, x);
    } 
    
    int main() {
    	n = read();
    	//按要求执行qwq 
    	for(int i = 1, opt, x; i <= n; i++) {
    		opt = read(), x = read();
    		if(opt == 1) insert(root, x);
    		else if(opt == 2) del(root, x);
    		else if(opt == 3) cout << rank(root, x) << '
    ';
    		else if(opt == 4) cout << num(root, x) << '
    ';
    		else if(opt == 5) ans = 0, pre(root, x), cout << tr[ans].v << '
    ';
    		else if(opt == 6) ans = 0, suc(root, x), cout << tr[ans].v << '
    ';
    	}
    	return 0;
    } 
    
  • 相关阅读:
    在蓝鲸标准运维上进行原子开发二
    python 使用eval报错NameError: name ‘null’ is not defined
    前端时间转换 2019-08-18T16:00:00.000Z 转换成格式2019-08-18
    python list排序
    Django 分页Paginator的简单用法
    蓝鲸ESB自定义组件的部署步骤
    Unity对象池的实现
    Unity3D UGUI实现Toast
    C#中扩展方法的使用
    Unity3D实现多语言切换
  • 原文地址:https://www.cnblogs.com/loceaner/p/12240718.html
Copyright © 2020-2023  润新知