bzoj 3224 Tyvj 1728 普通平衡树 (替罪羊树模板)

想吃什么


Sol

  • 正常的二叉搜索树+暴力重构
    lc,rc分别表示i的左右子树
    对以p为根的子树重构当且仅当

    siz[lc]>siz[p]alpha

    或者
    siz[rc]>siz[p]alpha

    alpha([0.5,1]) 是自己设定的一个参数,规定了这棵树的平衡程度
    alpha==0.5 时,几乎每次都要重构,树为平衡树,但是重构次数太多
    alpha==1.0 时,不会重构,此时是普通的二叉搜索树,有可能卡成链

  • 重构方法:先对以p为根的子树DFS(中序遍历),将序号存在一个数组里。因为是中序遍历,因此数组内点的值是有序的,可以直接build

#define lc ch[p][0]
#define rc ch[p][1]
void build(int &p,int L,int R){
    if(L>R){
        p=0;    //非常重要,将叶节点的信息清空,否则会形成图
        return;
    }
    int mid=(L+R)>>1;
    p=loc[mid];
    build(lc,L,mid-1);
    build(rc,mid+1,R);
    if(lc) fa[lc]=p;
    if(rc) fa[rc]=p;
    pushup(p);
}
void DFS(int p){
    if(lc) DFS(lc);
    loc[++sum]=p;
    if(rc) DFS(rc);
    //lc=rc=0;//也可以在这里清空节点信息
}

Code

// by spli
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#define lc ch[p][0]
#define rc ch[p][1]
using namespace std;

const int N=100010;
const int inf=0x3f3f3f3f;
const double alpha=0.75;
int n,m;
int rt=1,tot=1;
int loc[N],sum;
int ch[N][2],fa[N];
double siz[N];
int val[N];

inline void pushup(int p){
    siz[p]=siz[lc]+siz[rc]+1;
}

inline bool banlc(int p){
    if(siz[lc]>siz[p]*alpha) return 0;
    if(siz[rc]>siz[p]*alpha) return 0;
    return 1;
}

void build(int &p,int L,int R){
    if(L>R){
        p=0;    
        return;
    }
    int mid=(L+R)>>1;
    p=loc[mid];
    build(lc,L,mid-1);
    build(rc,mid+1,R);
    if(lc) fa[lc]=p;
    if(rc) fa[rc]=p;
    pushup(p);
}

void DFS(int p){
    if(!p) return;
    if(lc) DFS(lc);
    loc[++sum]=p;
    if(rc) DFS(rc);
}

void rebuild(int p){
    sum=0;
    DFS(p);
    int f0=fa[p];
    bool b=(ch[f0][0]!=p);
    int cur;
    build(cur,1,sum);
    fa[cur]=f0;
    ch[f0][b]=cur;
    if(p==rt) rt=cur;
}

void ins(int v){
    int p=rt,cur=++tot;
    siz[cur]=1;
    val[cur]=v;
    bool b;
    while(1){
        siz[p]++;
        b=(v>=val[p]);
        if(ch[p][b]) p=ch[p][b];
        else{
            ch[p][b]=cur;
            fa[cur]=p;
            break;
        }
    }
    int pos=0;
    for(int i=cur;i;i=fa[i]){
        if(!banlc(i)) pos=i;
    }
    if(pos) rebuild(pos);
}

inline int getpos(int p,int v){
    while(1){
        if(val[p]==v) return p;
        if(v>val[p]) p=rc;
        else p=lc;
    }
}

inline void del(int v){
    int p=getpos(rt,v);
    if(lc&&rc){
        int cur=lc;
        while(ch[cur][1]) cur=ch[cur][1];
        val[p]=val[cur];
        p=cur; 
    }
    int son=lc?lc:rc;
    int f0=fa[p];
    bool b=ch[f0][0]!=p;
    ch[f0][b]=son;
    fa[son]=f0;
    for(int i=fa[p];i;i=fa[i]) siz[i]--;
    if(p==rt) rt=son;
}

inline int getrank(int p,int v){
    int ret=0;
    while(p){
        if(v>val[p]) ret+=siz[lc]+1,p=rc;
        else p=lc;
    }
    return ret;
}

inline int kth(int p,int k){
    while(1){
        if(k<=siz[lc]) p=lc;
        else if(k==siz[lc]+1) return p;
        else k-=siz[lc],k--,p=rc;
    }
}

inline int getpre(int p,int v){
    int ret=-inf;
    while(p){
        if(v>val[p]) ret=max(ret,val[p]),p=rc;
        else p=lc;
    }
    return ret;
}

inline int getsuc(int p,int v){
    int ret=inf;
    while(p){
        if(v<val[p]) ret=min(ret,val[p]),p=lc;
        else p=rc;
    }
    return ret;
}

int main(){
    scanf("%d",&n);
    siz[rt]=1;
    val[rt]=-inf;
    int op,x;
    while(n--){
        scanf("%d%d",&op,&x);
        if(op==1) ins(x);
        if(op==2) del(x);
        if(op==3) printf("%d\n",getrank(rt,x));
        if(op==4) printf("%d\n",val[kth(rt,x+1)]);
        if(op==5) printf("%d\n",getpre(rt,x));
        if(op==6) printf("%d\n",getsuc(rt,x));
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值