平衡树--替罪羊树 *

平衡树–替罪羊树

–yangkai


身为平衡树却不做任何形式的旋转,替罪羊树可以称得上是最暴力的平衡树了。

替罪羊树(SGT)保留有二叉搜索树的基本性质,即对于任意一个节点t,左儿子的所有节点比它小,右儿子的所有节点比它大。但是既然不基于翻转,它怎样维护平衡树的优秀复杂度呢?

SDT基于一个叫做“重构”的操作,听起来很是优美暴力,那么我们要如何重构?

首先,如果要时刻维持平衡树的平衡,即abs(siz[l]siz[r])<=1,显然我们平均每两次插入、删除操作就需要一次重构,这样的时间效率接近O(n2),所以我们可以取一个阈值[0.5,1.0],当且仅当左右子树超过阈值的限制重构子树。在这里我们可以发现,当阈值限制越紧[0.5,0.75],对修改少询问多有利;当阈值限定越松[0.75,1.0],对修改多查询少有利。

根据阈值,我们就可以对子树进行重构了。

但是问题又来了,在条根到叶子的链上可能有许许多多的“不平衡点”,我们要怎样确定最优的一个,实际上,我们只需要选择最靠近根节点的一个就好了,因为如果修改的层数太深,在总体上不会对树的结构产生多么大的影响,所以选最靠近根的一个相对来说比较优秀。

那么维护平衡树性质的部分我们解决了,但是插入以及删除呢?

首先,对于插入,直接参见动态开点大法,在向下插入的时候动态开点就好了(此处应该注意回收内存)

其次,对于删除,我们既不能把它转到叶子结点(treap),也不能直接裂开丢掉再合并(非旋转treap),所以我们给它打上一个real标记,real标记存在说明这个节点信息为’真‘,否则为‘假’(已经删除),然后在重构子树的时候直接丢掉然后回收内存就好了

其他操作rank,get_kth,pre,nxt之类的操作直接套用平衡树常见套路就好了


然后写数组版的朋友们要注意
需要维护父亲和儿子的关系
不然会莫名其妙地丢失子树


例题:BZOJ3224 Tyvj 1728 普通平衡树

Description

您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
\1. 插入x数
\2. 删除x数(若有多个相同的数,因只删除一个)
\3. 查询x数的排名(若有多个相同的数,因输出最小的排名)
\4. 查询排名为x的数
\5. 求x的前驱(前驱定义为小于x,且最大的数)
\6. 求x的后继(后继定义为大于x,且最小的数)

Input

第一行为n,表示操作的个数,下面n行每行有两个数opt和x,opt表示操作的序号(1<=opt<=6)

Output

对于操作3,4,5,6每行输出一个数,表示对应答案

Sample Input

10
1 106465
4 1
1 317721
1 460929
1 644985
1 84185
1 89851
6 81968
1 492737
5 493598

Sample Output

106465
84185
492737

HINT

1.n的数据范围:n<=100000

2.每个数的数据范围:[-2e9,2e9]


上板子

//yangkai 
#include<bits/stdc++.h>
using namespace std;
inline int read(){
    int data=0,w=1;char ch=getchar();
    while(!isdigit(ch)&&ch!='-')ch=getchar();
    if(ch=='-')w=-1,ch=getchar();
    while(isdigit(ch))data=(data<<1)+(data<<3)+(ch-'0'),ch=getchar();
    return data*w;
}
struct SGT{
    static const int N=100010;
    static const double alpha=0.75;
    //内存池 
    int st[N],top,tot;
    int get_place(){return top?st[top--]:++tot;}
    void del_place(int t){st[++top]=t;}
    void clean_st(){top=tot=0;}
    //SGT
    int root;
    int ls[N],rs[N],fa[N];
    int val[N],siz[N],all[N];
    bool real[N];
    void pushup(int t){
        siz[t]=siz[ls[t]]+siz[rs[t]]+real[t];
        all[t]=all[ls[t]]+all[rs[t]]+1;
    }
    bool check(int t){//返回1说明需要重构 
        if(all[ls[t]]>=alpha*all[t])return 1;
        if(all[rs[t]]>=alpha*all[t])return 1;
        return 0;
    }
    int newnode(int vl=0,int f=0){
        int t=get_place();
        ls[t]=rs[t]=0;
        val[t]=vl;
        siz[t]=all[t]=1;
        real[t]=1;
        fa[t]=f;
        return t;
    }
    //收集需要重构的下标 
    void collect(int t,vector<int> &v){
        if(!t)return;
        collect(ls[t],v);
        if(real[t])v.push_back(t);
        else del_place(t);
        collect(rs[t],v); 
    }
    //重构划分 
    int divide(int l,int r,vector<int> v){
        if(l>=r)return 0;
        int mid=(l+r)>>1;
        int t=v[mid];
        ls[t]=divide(l,mid,v);
        rs[t]=divide(mid+1,r,v);
        fa[ls[t]]=fa[rs[t]]=t;//维护父亲信息
        pushup(t);
        return t;
    }
    //重新建树 
    void rebuild(int &t){
        static vector<int> v;v.clear();
        int f=fa[t];
        collect(t,v);
        t=divide(0,v.size(),v);
        fa[t]=f;//维护父亲信息
    }
    //查询vl的排名 
    int rank(int vl){
        int t=root,ans=1;
        while(t){
            if(vl<=val[t])t=ls[t];
            else{
                ans+=siz[ls[t]]+real[t];
                t=rs[t];
            }
        }
        return ans;
    }
    //查询排名为k的数
    int get_kth(int k){
        int t=root;
        while(t){
            if(siz[ls[t]]+1==k&&real[t])return val[t];
            if(siz[ls[t]]>=k)t=ls[t];
            else k-=siz[ls[t]]+real[t],t=rs[t];
        }
    } 
    //插入vl值 
    int insert(int &t,int f,int vl){//返回深度最浅的不合法点 
        if(!t){t=newnode(vl,f);return 0;}
        int res;
        if(vl<=val[t])res=insert(ls[t],t,vl);
        else res=insert(rs[t],t,vl);
        pushup(t);
        //检查是否需要重构 
        if(check(t))res=t;
        return res;
    }
    void insert(int vl){
        int t=insert(root,0,vl);
        if(!t)return;
        if(t==root)rebuild(root);
        else{
            int f=fa[t];
            if(t==ls[f])rebuild(ls[f]);
            else rebuild(rs[f]);
        }
    }
    //删除排名为k的数 
    void erase(int t,int k){
        siz[t]--;
        if(real[t]&&k==siz[ls[t]]+real[t]){real[t]=0;return;}
        if(k<=siz[ls[t]])erase(ls[t],k);
        else erase(rs[t],k-siz[ls[t]]-real[t]);
    }
    //删除数vl 
    void erase(int vl){
        erase(root,rank(vl));
        if(siz[root]<alpha*all[root])rebuild(root);
    }
    //查询vl的前驱和后继 
    int pre(int vl){return get_kth(rank(vl)-1);} 
    int nxt(int vl){return get_kth(rank(vl+1));}
    //输出调试
    void out_put(int t){
        cout<<t<<"||"<<endl;
        if(ls[t])out_put(ls[t]);
        if(real[t])cout<<val[t]<<" L:"<<val[ls[t]]<<" R:"<<val[rs[t]]<<endl;
        if(rs[t])out_put(rs[t]);
    } 
}sgt;
int main(){
    int n=read();
    while(n--){
        int op=read(),t=read();
        switch(op){
            case 1:sgt.insert(t);break;
            case 2:sgt.erase(t);break;
            case 3:printf("%d\n",sgt.rank(t));break;
            case 4:printf("%d\n",sgt.get_kth(t));break;
            case 5:printf("%d\n",sgt.pre(t));break;
            case 6:printf("%d\n",sgt.nxt(t));break;
        }
    }
    return 0;
} 
posted @ 2018-06-03 14:22  Dream_maker_yk  阅读(172)  评论(0编辑  收藏  举报