zoukankan      html  css  js  c++  java
  • 替罪羊树 知识总结

    替罪羊树 知识总结

    替罪羊树是一类神奇的平衡树。它最神奇的地方就在于,大部分平衡树都是用愚蠢的单旋来维护平衡,而fhq-Treap则是用split和merge维护平衡的,可替罪羊树却是用一种神奇的操作维护平衡的,那就是重构Rebuild。每次插入和删除元素的时候,检查子树大小,若失衡则直接重购以维护整棵树平衡。
    在讲思路前,我们先要注意一个点:替罪羊树仅能用重构来维持平衡,因而若节点被删除后,无法将其高效的移除,所以我们采取懒惰删除,每次仅将节点的计数器--即可。
    先来看一下我们的变量区和基础的函数吧:
    struct node{
    	int l,r;//左右儿子
    	int sd,sz;//sd为节点所在子树的实际大小,而sz则为包括所有删除的元素在内的子树大小
    	int cnt,val;//cnt为数量,val为键值
    };
    inline void update(int p)
    {
    	f[p].sd=f[f[p].l].sd+f[f[p].r].sd+f[p].cnt;
    	f[p].sz=f[f[p].l].sz+f[f[p].r].sz+f[p].cnt;
    }
    
    接下来我们来看看重构操作吧。我们预先设定一个阈值α,一般取[0.7,0.8]左右,若检查到某个节点的儿子所在子树的sz占这个节点sz的比例超过α时,就重构。由于当被删除节点过多时,搜索树效率也会显著降低,于是当一个节点的sd占其sz的比例超过α时,亦重构。
    inline bool Can_Rbu(int p)
    {
    	return f[p].cnt&&(f[p].sz*alpha<=(double)max(f[f[p].l].sz,f[f[p].r].sz)||f[p].sz*alpha>=(double)f[p].sd);
    }
    
    那么怎么重构呢?我们已经知道了,对于一棵平衡树,它的中序遍历是排好序的。所以我们把它的中序遍历拉到数组中,然后重新二分建树即可:
    int tmp[100010];
    void Rbu_Flatten(int &num,int p)
    {
    	if(!p) return;//空儿子返回
    	Rbu_Flatten(num,f[p].l);//搜左儿子
    	if(f[p].cnt) tmp[num++]=p;//加入当前节点
    	Rbu_Flatten(num,f[p].r);//搜右儿子
    }
    int Rbu_Build(int l,int r)
    {
    	if(l>=r) return 0;//二分边界
    	int mid=(l+r)>>1;
    	f[tmp[mid]].l=Rbu_Build(l,mid);//建左儿子
    	f[tmp[mid]].r=Rbu_Build(mid+1,r);//建右儿子
    	update(tmp[mid]);//更新信息
    	return tmp[mid];
    }
    void Rbu(int &p)
    {
    	int x=0;
    	Rbu_Flatten(x,p);
    	p=Rbu_Build(0,x);//这里注意下标从0开始可以节省很多事情
    }
    
    以上就是基本函数啦!其余的部分神似普通的平衡树,我们的介绍和注释就稍微简略一些了咯。
    首先是插入和删除。我们在遍历路径上每一个节点时,都检查是否满足重构条件,并重构即可。其余与普通二叉搜索时雷同。
    void Insert(int &p,int val)
    {
    	if(!p)
    	{
    		p=++tot;
    		f[p].cnt=f[p].sd=f[p].sz=1;
    		f[p].val=val;
    		f[p].l=f[p].r=0;
    	}
    	else
    	{
    		if(f[p].val==val) f[p].cnt++;
    		else if(val<f[p].val) Insert(f[p].l,val);
    		else Insert(f[p].r,val);
    		update(p);
    		if(Can_Rbu(p)) Rbu(p);
    	}
    }
    void del(int &p,int val)
    {
    	if(!p) return;
    	f[p].sd--;
    	if(f[p].val==val)
    	{
    		if(f[p].cnt) f[p].cnt--;
    	}
    	else
    	{
    		if(val<f[p].val) del(f[p].l,val);
    		else del(f[p].r,val);
    		update(p);
    	}
    	if(Can_Rbu(p)) Rbu(p);
    }
    
    接下来是由权值查询对应的值。依旧是与普通二叉搜索树完全相同的操作,在此不再赘述了,不懂直接看代码吧QAQ
    int GetVal(int p,int rk)
    {
    	if(!p) return 0;
    	if(rk>f[f[p].l].sd&&rk<=f[f[p].l].sd+f[p].cnt) return f[p].val;
    	else if(rk>f[f[p].l].sd+f[p].cnt) return GetVal(f[p].r,rk-f[f[p].l].sd-f[p].cnt);
    	else return GetVal(f[p].l,rk);
    }
    
    最后是查询前后驱的函数。在此我们稍微的改变一下函数的返回值:将返回数值改为返回下标。这样以后查询排名就可以用前驱坐标+1即可。代码依旧与普通平衡树相同……
    int Get_uprb(int p,int val)
    {
    	if(!p) return 1;
    	if(f[p].val==val&&f[p].cnt) return f[f[p].l].sd+f[p].cnt+1;
    	else if(val<f[p].val) return Get_uprb(f[p].l,val);
    	else return f[f[p].l].sd+f[p].cnt+Get_uprb(f[p].r,val);
    }
    int Get_lwrb(int p,int val)
    {
    	if(!p) return 0;
    	if(f[p].val==val&&f[p].cnt) return f[f[p].l].sd;
    	else if(val<f[p].val) return Get_lwrb(f[p].l,val);
    	else return f[f[p].l].sd+f[p].cnt+Get_lwrb(f[p].r,val);
    }
    
    最后就是完整版代码啦:
    #include<bits/stdc++.h>
    using namespace std;
    template <typename T>
    void read(T &x) {
        x = 0;
        int f = 1;
        char ch = getchar();
        while (!isdigit(ch)) {
            if (ch == '-') f = -1;
            ch = getchar();
        }
        while (isdigit(ch)) {
            x = x * 10 + (ch ^ 48);
            ch = getchar();
        }
        x *= f;
        return;
    }
    template <typename T>
    void write(T x)
    {
        if(x < 0) {
            putchar('-');
            x = -x;
        }
        if(x > 9)
            write(x/10);
        putchar(x % 10 + '0');
        return;
    }
    int n;
    int rt,tot=0;
    struct node{
    	int l,r;
    	int sd,sz;
    	int cnt,val;
    }f[100010];
    const double alpha=0.75;
    inline void update(int p)
    {
    	f[p].sd=f[f[p].l].sd+f[f[p].r].sd+f[p].cnt;
    	f[p].sz=f[f[p].l].sz+f[f[p].r].sz+f[p].cnt;
    }
    inline bool Can_Rbu(int p)
    {
    	return f[p].cnt&&(f[p].sz*alpha<=(double)max(f[f[p].l].sz,f[f[p].r].sz)||f[p].sz*alpha>=(double)f[p].sd);
    }
    int tmp[100010];
    void Rbu_Flatten(int &num,int p)
    {
    	if(!p) return;
    	Rbu_Flatten(num,f[p].l);
    	if(f[p].cnt) tmp[num++]=p;
    	Rbu_Flatten(num,f[p].r);
    }
    int Rbu_Build(int l,int r)
    {
    	if(l>=r) return 0;
    	int mid=(l+r)>>1;
    	f[tmp[mid]].l=Rbu_Build(l,mid);
    	f[tmp[mid]].r=Rbu_Build(mid+1,r);
    	update(tmp[mid]);
    	return tmp[mid];
    }
    void Rbu(int &p)
    {
    	int x=0;
    	Rbu_Flatten(x,p);
    	p=Rbu_Build(0,x);
    }
    void Insert(int &p,int val)
    {
    	if(!p)
    	{
    		p=++tot;
    		f[p].cnt=f[p].sd=f[p].sz=1;
    		f[p].val=val;
    		f[p].l=f[p].r=0;
    	}
    	else
    	{
    		if(f[p].val==val) f[p].cnt++;
    		else if(val<f[p].val) Insert(f[p].l,val);
    		else Insert(f[p].r,val);
    		update(p);
    		if(Can_Rbu(p)) Rbu(p);
    	}
    }
    void del(int &p,int val)
    {
    	if(!p) return;
    	f[p].sd--;
    	if(f[p].val==val)
    	{
    		if(f[p].cnt) f[p].cnt--;
    	}
    	else
    	{
    		if(val<f[p].val) del(f[p].l,val);
    		else del(f[p].r,val);
    		update(p);
    	}
    	if(Can_Rbu(p)) Rbu(p);
    }
    int Get_uprb(int p,int val)
    {
    	if(!p) return 1;
    	if(f[p].val==val&&f[p].cnt) return f[f[p].l].sd+f[p].cnt+1;
    	else if(val<f[p].val) return Get_uprb(f[p].l,val);
    	else return f[f[p].l].sd+f[p].cnt+Get_uprb(f[p].r,val);
    }
    int Get_lwrb(int p,int val)
    {
    	if(!p) return 0;
    	if(f[p].val==val&&f[p].cnt) return f[f[p].l].sd;
    	else if(val<f[p].val) return Get_lwrb(f[p].l,val);
    	else return f[f[p].l].sd+f[p].cnt+Get_lwrb(f[p].r,val);
    }
    int GetVal(int p,int rk)
    {
    	if(!p) return 0;
    	if(rk>f[f[p].l].sd&&rk<=f[f[p].l].sd+f[p].cnt) return f[p].val;
    	else if(rk>f[f[p].l].sd+f[p].cnt) return GetVal(f[p].r,rk-f[f[p].l].sd-f[p].cnt);
    	else return GetVal(f[p].l,rk);
    }
    int main()
    {
    	read(n);
    	for(int i=1;i<=n;i++)
    	{
    		int op;
    		read(op);
    		int x;
    		read(x);
    		switch(op)
    		{
    			case 1:
    				Insert(rt,x);break;
    			case 2:
    				del(rt,x);break;
    			case 3:
    				write(Get_lwrb(rt,x)+1);putchar('
    ');break;
    			case 4:
    				write(GetVal(rt,x));putchar('
    ');break;
    			case 5:
    				write(GetVal(rt,Get_lwrb(rt,x)));putchar('
    ');break;
    			case 6:
    				write(GetVal(rt,Get_uprb(rt,x)));putchar('
    ');break;
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    HDU 1022 Train Problem I
    HDU 1702 ACboy needs your help again!
    HDU 1294 Rooted Trees Problem
    HDU 1027 Ignatius and the Princess II
    HDU 3398 String
    HDU 1709 The Balance
    HDU 2152 Fruit
    HDU 1398 Square Coins
    HDU 3571 N-dimensional Sphere
    HDU 2451 Simple Addition Expression
  • 原文地址:https://www.cnblogs.com/xiaoh105/p/12163392.html
Copyright © 2011-2022 走看看