• 替罪羊树


    这是一篇低质量博客

    但是笔者自己看效果可能很好(写给自己看的博客)

    一、前言

    失踪人口终于回归啦~

    替罪羊树太简单了! 以致于板题调了两天呢!

    二、讲解

    我们已经学过很多平衡树了,对于平衡树满足的特点,我就不多赘述了

    我们直接步入正题,讲解替罪羊树维持平衡的方法

    首先我们要知道怎样才算不平衡,这样我们才好修复

    直接yy:如果一个点的某一个儿子太重了,那么这棵子树就不平衡了

    上面的yy过于感性,我们来定量分析一下:

    我们引入替罪羊树专有的变量:(alpha)平衡因子

    如果重儿子的重量 (>) 整棵子树的重量 ( imes alpha),那么我们认为它不平衡

    我们一般把(alpha)定为(0.5)~(1)的数(()个人喜欢(0.7))

    别问,问就是7777777777777777

    那么如果树不平衡了怎么办呢?

    暴力重建!

    当然不是整棵树,而是不平衡的最上面的那棵子树

    比如子树(A,B)都不平衡,而子树(B)属于子树(A),那么我们要重建的是子树(A)而非子树(B),因为重建了子树(B),子树(A)依然不平衡

    由于只有插入和删除操作会影响,所以在每次插入或删除操作后,我们检查一下是否存在不平衡的子树,有则改之,无则加勉

    检查的细节是插入和删除只有一个元素,所以只需要检查 以这个元素到根的路径上的点的子树是否平衡就行了

    重建没什么好说的吧,把要重建的点拖出来重新排一下就好了

    0、结构体定义

    int son[2],val,siz,cnt,fa;
    //左右儿子(0,1) 元素值 子树大小 元素个数 当前节点的父亲
    bool exist;
    //是否存在(其实直接看cnt是否为0就好了,我码麻烦了)
    

    1、插入

    其实插入没什么好讲的

    如果存在插入的元素,(cnt++)

    不存在,找到应该放的位置,放进去就好了

    插入之后记得更新这条链上的子树大小

    2、删除

    删除有两种方法

    一是打上标记(貌似不需要打标记,只需要检查次数就好了)

    二是把它的儿子提上来

    我是前者

    找到该元素,(cnt--)

    如果(cnt==0),打上消失的标记(也可以不打,我们可以根据(cnt)是否为(0)来判断)

    记得更新这条链上的子树大小

    3、其他操作

    和其它的平衡树类似,不再展开

    关于代码:笔者首次采用了内存池的打法,个人感觉这种写法很好,值得一学(你不学我也不拦着你)

    三、练习

    板题(洛谷)

    带插入区间K小值(洛谷)

    四、代码

    板题代码

    //12252024832524
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std; 
    
    typedef long long LL;
    const int MAXN = 100005;
    const int INF = 0x3f3f3f3f; 
    int n;
    
    int Read()
    {
    	int x = 0,f = 1;char c = getchar();
    	while(c > '9' || c < '0'){if(c == '-')f = -1;c = getchar();}
    	while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
    	return x * f;
    }
    void Put1(int x)
    {
    	if(x > 9) Put1(x/10);
    	putchar(x%10^48);
    }
    void Put(int x,char c = -1)
    {
    	if(x < 0) putchar('-'),x = -x;
    	Put1(x);
    	if(c >= 0) putchar(c);
    }
    template <typename T>T Max(T x,T y){return x > y ? x : y;}
    template <typename T>T Min(T x,T y){return x < y ? x : y;}
    template <typename T>T Abs(T x){return x < 0 ? -x : x;}
    
    struct ScapegoatTree
    {
    	int rt,tot,ncc[MAXN],ncctot;//根 点 内存池 内存池的点 
    	int retot,re[MAXN][2];//0 val 1 cnt
    	double alpha = 0.7; //平衡因子 
    	
    	struct node
    	{
    		int son[2],val,siz,cnt,fa;
    		bool exist;
    	}t[MAXN];
    	
    	int newnode()
    	{
    		if(ncctot > 0) return ncc[ncctot--];
    		return ++tot;
    	}
    	int find(int x,int val)
    	{
    		if(t[x].val > val && t[x].son[0]) return find(t[x].son[0],val);
    		if(t[x].val < val && t[x].son[1]) return find(t[x].son[1],val);
    		return x;
    	}
    	void build(int x,int val,int fa)
    	{
    		t[x].son[0] = t[x].son[1] = 0;
    		t[x].fa = fa;
    		t[x].exist = 1;
    		t[x].val = val;
    		t[x].siz = t[x].cnt = 1;
    	}
    	void update(int x)
    	{
    		if(!x) return ;
    		t[x].siz = t[t[x].son[0]].siz + t[t[x].son[1]].siz + t[x].cnt;
    		update(t[x].fa);
    	}
    	void redfs(int x)
    	{
    		if(!x) return;
    		redfs(t[x].son[0]);
    		if(t[x].exist) re[++retot][0] = t[x].val,re[retot][1] = t[x].cnt;
    		ncc[++ncctot] = x;
    		redfs(t[x].son[1]);
    	}
    	int renow(int l,int r,int fa)
    	{
    		if(l > r) return 0;
    		int ID = newnode(),mid = (l+r) >> 1;
    		t[ID].val = re[mid][0];
    		t[ID].cnt = re[mid][1];
    		t[ID].fa = fa;
    		t[ID].son[0] = renow(l,mid-1,ID);
    		t[ID].son[1] = renow(mid+1,r,ID);
    		t[ID].siz = t[t[ID].son[0]].siz + t[t[ID].son[1]].siz + t[ID].cnt;
    		t[ID].exist = 1;
    		return ID; 
    	}
    	void rebuild(int x)
    	{
    		retot = 0;
    		redfs(x);
    		if(x == rt) rt = renow(1,retot,0);
    		else
    		{
    			if(t[t[x].fa].son[0] == x) t[t[x].fa].son[0] = renow(1,retot,t[x].fa);
    			else t[t[x].fa].son[1] = renow(1,retot,t[x].fa);
    			update(t[x].fa);
    		}
    	}
    	void find_rebuild(int x,int val)
    	{
    		if(!x) return;
    		if(1.0 * Max(t[t[x].son[0]].siz,t[t[x].son[1]].siz) > t[x].siz * alpha) {rebuild(x);return;}
    		if(t[x].val != val) find_rebuild(t[x].son[val > t[x].val],val);
    	}
    	void ins(int val)
    	{
    		if(!rt)
    		{
    			build(rt = newnode(),val,0);
    			return;
    		}
    		int dz = find(rt,val);
    		if(val == t[dz].val)
    		{
    			t[dz].cnt++;
    			t[dz].exist = 1;
    			update(dz);
    		}
    		else
    		{
    			if(val < t[dz].val) t[dz].son[0] = newnode(),build(t[dz].son[0],val,dz),update(t[dz].son[0]);
    			else t[dz].son[1] = newnode(),build(t[dz].son[1],val,dz),update(t[dz].son[1]);
    		}
    		find_rebuild(rt,val);
    	}
    	void del(int val)
    	{
    		int dz = find(rt,val);
    		if(t[dz].val == val) 
    		{
    			t[dz].cnt --;
    			if(!t[dz].cnt) t[dz].exist = 0;
    			update(dz);
    			find_rebuild(rt,val);
    		}
    	}
    	int rk(int val)
    	{
    		int now = rt,ret = 0;
    		while(t[now].val != val && now)
    		{
    			if(t[now].val < val) ret += t[now].cnt + t[t[now].son[0]].siz,now = t[now].son[1];
    			else now = t[now].son[0];
    		}
    		ret += t[t[now].son[0]].siz;
    		return ret + 1;
    	}
    	int findrk(int x) 
    	{
    		int now = rt;
    		while(1)
    		{
    			if(t[t[now].son[0]].siz >= x) now = t[now].son[0];
    			else if(t[t[now].son[0]].siz + t[now].cnt < x) x -= t[t[now].son[0]].siz + t[now].cnt,now = t[now].son[1];
    			else return t[now].val;
    		}
    	}
    	int pre(int val)
    	{
    		return findrk(rk(val)-1);
    	} 
    	int suc(int val)
    	{
    		return findrk(rk(val+1));
    	}
    	void debug(int x)
    	{
    		if(!x) return;
    		debug(t[x].son[0]);
    		if(t[x].exist)
    			Put(x,' '),Put(t[x].val,'
    ');
    		debug(t[x].son[1]);
    //		update(x);
    	}
    }s;
    
    int main()
    {
    //	freopen(".in","r",stdin);
    //	freopen(".out","w",stdout);
    	for(int T = Read(); T ;-- T)
    	{
    		int opt = Read(),x = Read();
    		if(opt == 1) s.ins(x);//插入x
    		else if(opt == 2) s.del(x);//删除x
    		else if(opt == 3) Put(s.rk(x),'
    ');//查询x的rank
    		else if(opt == 4) Put(s.findrk(x),'
    ');//查询rank为x的数 
    		else if(opt == 5) Put(s.pre(x),'
    ');//查询x的前缀 
    		else Put(s.suc(x),'
    ');//查询x的后缀 
    	}
    	return 0;
    }
    
  • 相关阅读:
    c++教程目录
    ANDROID教程目录
    ArrayBuffer
    读懂 ECMAScript 规格
    编程风格
    Module 的加载实现
    Module 的语法
    修饰器Decorator
    Class 的继承
    SQL Server 2008 R2导出数据脚本的方法
  • 原文地址:https://www.cnblogs.com/PPLPPL/p/13392267.html
Copyright © 2020-2023  润新知