[学习笔记]替罪羊树

替罪羊树——简单粗暴的数据结构 - a_forever_dream的..._CSDN博客

一种简单粗暴的数据结构(我自认为暴力“不优雅”。。。)

和其他“优雅”的splay,fhq-treap不同

替罪羊既不旋转,也不分裂合并

我看谁不顺眼,直接让其暴力重构!

 

思路就是这样。

基础

特点是:

1.如果一个点的左子树或者右子树的节点个数>x的节点个数*alpha(0.5~1.0之间的一个值),那么暴力重构整个子树

2.懒惰删除,因为SGT不够灵活。对删除的点打上标记,并且在祖先节点更新信息。但是不删掉节点本身(名存实亡)。

3.如果一个点子树的已经删除部分占30%左右,暴力重构子树

所以,一个点的信息是:

sz:子树节点个数

cnt:子树实际存在的节点个数

val:自己的权值

ch[2]:两个儿子

die:是否被删除

 

先说暴力重构:

分三步:

1.dfs(x),把x子树所有没有删除的点加入一个数组里

2.build(l,r),分治建树

3.pushup(fa[x])把x的father的信息更新一下(node中可以不记录father,临时记录即可)

 

操作:

(最大的坑莫过于未考虑到名存实亡的点)

insert:类比splay,while循环,找到位置之后加入节点(可以同一个值分成不同的点,x和左右子树的关系变成不严格的即可)

    最后从上而下尝试rebuild

dele:找到后die=1,

    注意:必须找到一个没有被删除的。所以,不用权值找,而用第k大(坑了2h)

    (权值的话,如果x被删除了你不知道往哪里走)

    最后从上而下尝试rebuild

rank,kth,pre,bac都差不多,kth的返回,注意当前点必须是未删除点

 

复杂度:alpha调好,可以保证O(nlogn)(alpha一般是0.75)

 

优劣

优点:

1.常数小。不知为啥

2.结构稳定,一些不能大量旋转或者分裂的时候,只能SGT

3.暴力重构的思想值得借鉴

缺点:

1.不够灵活,区间操作无能为力

2.一些题目名存实亡的点不好处理。且容易写挂。

 

代码:

P3369 【模板】普通平衡树 

 

#include<bits/stdc++.h>
#define reg register int
#define il inline
#define fi first
#define se second
#define mk(a,b) make_pair(a,b)
#define numb (ch^'0')
#define pb push_back
#define solid const auto &
#define enter cout<<endl
#define pii pair<int,int>
using namespace std;
typedef long long ll;
template<class T>il void rd(T &x){
    char ch;x=0;bool fl=false;
    while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
    for(x=numb;isdigit(ch=getchar());x=x*10+numb);
    (fl==true)&&(x=-x);
}
template<class T>il void output(T x){if(x/10)output(x/10);putchar(x%10+'0');}
template<class T>il void ot(T x){if(x<0) putchar('-'),x=-x;output(x);putchar('\n');}
template<class T>il void prt(T a[],int st,int nd){for(reg i=st;i<=nd;++i) ot(a[i]);putchar('\n');}

namespace Miracle{
const int N=1e5+5;
const double alp=0.75;
int n,tot,rt;
struct node{
    int ch[2];
    int sz,cnt,val;
    int die;
}t[N];
int nc(int v){
    ++tot;t[tot].ch[0]=t[tot].ch[1]=0;
    t[tot].sz=t[tot].cnt=1;t[tot].die=0;
    t[tot].val=v;return tot;
}
#define ls t[x].ch[0]
#define rs t[x].ch[1]
void pushup(int x){
    if(!x) return;
    t[x].sz=t[ls].sz+t[rs].sz+1;
    t[x].cnt=t[ls].cnt+t[rs].cnt+(!t[x].die);
}
int q[N],num;
bool isbad(int x){
    return (t[ls].sz>t[x].sz*alp||t[rs].sz>t[x].sz*alp||t[x].cnt<t[x].sz*alp);
}
void dfs(int x){
    if(!x) return ;
    dfs(ls);
    if(!t[x].die) q[++num]=x;
    dfs(rs);
}
int build(int l,int r){
    if(l>r) return 0;
    int mid=(l+r)>>1;
    int x=q[mid];
    ls=build(l,mid-1);
    rs=build(mid+1,r);
    pushup(x);
    return x;
}
void rebuild(int &x){
    num=0;dfs(x);
    x=build(1,num);
    pushup(x);
}
int sta[N],top;
void che(){
    reg i;
    for( i=1;i<=top;++i){
        if(isbad(sta[i])){
           if(sta[i]==rt) {
               rebuild(rt);
           }else{
               rebuild(t[sta[i-1]].ch[t[sta[i-1]].ch[1]==sta[i]]);pushup(sta[i-1]);
           } break;
        }
    }
}
void ins(int v){
    if(!rt){
        rt=nc(v);return;
    }
    int x=rt;
    top=0;
    while(1){
        int d=t[x].val<=v;
        ++t[x].sz;
        ++t[x].cnt;
        sta[++top]=x;
        if(!t[x].ch[d]){
            t[x].ch[d]=nc(v);
            break;
        }
        x=t[x].ch[d];
    }
    che();
}
int kth(int k){
    int x=rt;
    while(1){
        int d=t[t[x].ch[0]].cnt+(!t[x].die);
        if(!t[x].die&&d==k) return t[x].val;
        if(t[t[x].ch[0]].cnt>=k) x=t[x].ch[0];
        else{
            k-=d;x=t[x].ch[1];
        }
    }
    return -23333333;
}
int rk(int v){
    int x=rt;
    int ret=1;
    while(x){
        int d=t[x].val<v;
        if(d) ret+=t[t[x].ch[0]].cnt+(!t[x].die),x=t[x].ch[1];
        else x=t[x].ch[0];
    }
    return ret;
}
void dele(int v){
    int k=rk(v);
    int x=rt,top=0;
    while(1){
        int d=t[ls].cnt+(!t[x].die);
        sta[++top]=x;
        --t[x].cnt;
        if(!t[x].die&&d==k) {
            t[x].die=1;break;
        }
        if(t[ls].cnt>=k) x=ls;
        else {
            k-=d;x=rs;
        }
    }
    che();
}
int pre(int v){
    return kth(rk(v)-1);
}   
int bac(int v){
    return kth(rk(v+1));
}
int main(){
    rd(n);
    int op,x;
    while(n--){
        rd(op);rd(x);
        switch(op){
            case 1:ins(x);break;
            case 2:dele(x);break;
            case 3:printf("%d\n",rk(x));break;
            case 4:printf("%d\n",kth(x));break;
            case 5:printf("%d\n",pre(x));break;
            case 6:printf("%d\n",bac(x));break;
        }
    }
    return 0;
}

}
signed main(){
    Miracle::main();
    return 0;
}

/*
   Author: *Miracle*
*/
View Code

 

posted @ 2019-02-05 22:11  *Miracle*  阅读(216)  评论(0编辑  收藏  举报