• 【luogu P3369 【模板】普通平衡树(Treap/SBT)】 模板 Scapegoat Tree


    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define alpha 0.8
    #define maxn 2000001
    #define ri register
    #define il inline
    using namespace std;
    struct scapegoat{
    	int son[2], val, valid, total;//valid(有效的)未被删除的子树的点数 total(总数)子树总点数 
    	bool exist;//是否要被删除 exist(存在)1表示未被删除 0表示被删除 
    }e[maxn];
    int memory[maxn]; //内存池 
    int cur[maxn]; //拍扁的时候用的内存空间
    int root, pool, poi, cnt, to_rebuild;
    il bool isbad(int now)
    {
    	if((double)e[now].valid*alpha <= (double)max(e[e[now].son[0]].valid, e[e[now].son[1]].valid)) return true;
    	return false;
    }
    void dfs(int now) // 中序遍历,找出要被拍扁的节点的编号 
    {
    	if(!now) return;
    	dfs(e[now].son[0]);
    	if(e[now].exist) cur[++poi] = now;
    	else memory[++pool] = now;
    	dfs(e[now].son[1]);
    }
    void build(int l, int r, int &now) //你建树值要跟着变的...now当然加&了... 
    {
    	int mid = l+r>>1;//其实建树的序列已经按顺序保存在cur里了,你只需要改变父子关系就行 
    	now = cur[mid];//cur里存的是编号.把中间的元素取出来,中间元素的编号为now. 
    	if(l == r)//只造一个节点..? 
    	{
    		e[now].son[0] = e[now].son[1] = 0;
    		e[now].total = e[now].valid = 1;
    		return;	
    	}
    	if(l < mid) build(l,mid-1,e[now].son[0]);//到mid-1是因为mid已经建完了 
    	else e[now].son[0] = 0;
    	build(mid+1,r,e[now].son[1]);//左右递归建树 
    	e[now].total = e[e[now].son[0]].total + e[e[now].son[1]].total + 1;//更新节点信息 
    	e[now].valid = e[e[now].son[0]].valid + e[e[now].son[1]].valid + 1;
    }
    il void rebuild(int &now)
    {
    	poi = 0;//别忘了你重建的子树要从头开始算啊,不清零..就听取WA声一片 
    	dfs(now);//中序遍历一遍 
    	if(poi) build(1,poi,now);
    	else now = 0;
    }
    il int find_rank(int k)//寻找k的排名 
    {
    	int now = root;
    	int ans = 1;
    	while(now)
    	{
    		if(e[now].val >= k) now = e[now].son[0];
    		else
    		{
    			ans += e[e[now].son[0]].valid + e[now].exist;//+e[now].exist是因为我相同大小的节点虽然放在一起,但是我不知道我这个节点上相同的我是不是还存在啊..所以我得单独加我..至于valid是除我以外的子树大小。 
    			now = e[now].son[1];
    		}
    	}
    	return ans;
    }
    il int find_kth(int k)
    {
    	int now = root;
    	while(now) 
    	{
    		if(e[now].exist&&e[e[now].son[0]].valid+1 == k) return e[now].val;
    		else if(e[e[now].son[0]].valid >= k) now = e[now].son[0];
    		else
    		{
    			k -= e[e[now].son[0]].valid + e[now].exist;
    			now = e[now].son[1];
    		}
    	}
    }
    void insert(int &now, int val)
    {
    	if(!now)//找到一个插入的位置 
    	{
    		now = memory[pool--]; e[now].val = val;
    		e[now].exist = e[now].total = e[now].valid = 1;
    		e[now].son[0] = e[now].son[1] = 0;
    		return;
    	}
    	e[now].total++, e[now].valid++;//一边向下一边更新,这点与spaly不同 
    	if(e[now].val >= val) insert(e[now].son[0], val); 
    	else insert(e[now].son[1], val);
    	if(!isbad(now))
    	{
    		if(to_rebuild)
    		{
    			if(e[now].son[0] == to_rebuild) rebuild(e[now].son[0]);
    			else rebuild(e[now].son[1]);
    			to_rebuild = 0;
    		}
    	}
    	else to_rebuild = now;
    }
    il void delete_pos(int &now, int tar) //target(目标)
    {
    	if(e[now].exist&&e[e[now].son[0]].valid+ 1 == tar)//删除位置为tar的.. 
    	{
    		e[now].exist = 0; e[now].valid--; return;
    	}
    	e[now].valid--;
    	if(e[e[now].son[0]].valid + e[now].exist >= tar) delete_pos(e[now].son[0], tar);
    	else delete_pos(e[now].son[1],tar-e[e[now].son[0]].valid-e[now].exist);
    }
    il void delete_val(int tar)
    {
    	delete_pos(root, find_rank(tar));
    	if((double)e[root].total*alpha > e[root].valid) rebuild(root);
    }
    int main()
    {
    	int opt, x, m;
    	for(int i = 2000000; i >= 1; i--) memory[++pool] = i;
    	scanf("%d",&m);
    	while(m--)
    	{
    		scanf("%d%d",&opt,&x);
    		if(opt == 1) {insert(root, x);}
            if(opt == 2) {delete_val(x);}
            if(opt == 3) {printf("%d
    ",find_rank(x));}
            if(opt == 4) {printf("%d
    ",find_kth(x));}
            if(opt == 5) {printf("%d
    ",find_kth(find_rank(x)-1));}
            if(opt == 6) {printf("%d
    ",find_kth(find_rank(x+1)));}
    	}
    	//哇这太恐怖了我的替罪羊树他还能查没有插入的节点的排名...(好像是对于没插入过的节点会假装插进去了查询一个排名..但下次操作不会当做真插入了来做)
    	return 0;
    }
    
  • 相关阅读:
    常用linux命令
    console页面进去太慢优化
    CentOS7 查看最大线程连接数
    外部ssh连接Ubuntu系统
    Ubantu 防火墙管理
    oracle 闪回
    oracle用户密码过期
    base64编码原理
    Linux 备份数据库mysql
    python静态方法-类方法
  • 原文地址:https://www.cnblogs.com/MisakaAzusa/p/9226428.html
Copyright © 2020-2023  润新知