zoukankan      html  css  js  c++  java
  • 【替罪羊树】bzoj3224&luogu3369&cogs1829 [Tyvj 1728]普通平衡树

    【替罪羊树】bzoj3224&luogu3369&cogs1829 [Tyvj 1728]普通平衡树


    bzoj

    洛谷

    cogs


    先长点芝士


    替罪羊树也是一种很好写的平衡树qwq。。替罪羊树的核心思想就是重构。即当一棵子树的平衡被破坏,那么就把这棵树拍平,也就是树高为O(logn)的完美二叉树形态。这样看似复杂度很高,实则不然。可以证明,替罪羊树每次重构的复杂度都是均摊O(logn)的。
    ——蒯自WFJdalao的博客


    插入和普通BST类似,只需要判断插入操作是否导致了这一条链上结点的大小平衡被破坏,如果有的话将深度最浅的点所在子树暴力重建。重建最简单的方法就是对这棵子树中序遍历后分治建树。通过势能分析可以证明每一次插入的均摊复杂度为(logn)
    查询和普通BST并无区别。
    删除操作比较巧妙。对于一次删除,我们并不马上移除这个点,而是直接这个点上打上删除标记,查询时跳过该点。重构时可以顺便删除打了删除标记的点。当被删除结点的个数超过总结点数的((1−α))倍时可以选择重构整棵树进行结构优化,并且显然这部分重构的复杂度不会超过(O(nlogn))
    ——蒯自xlightgod学长的博客


    题解

    上面说的很清楚了,然鹅我并不会“势能分析”呵呵哒
    因为写treap写习惯了指针还挺好用的就尝试用指针写替罪羊树。。。我错了
    然后我深陷二级指针的泥坑中无法自拔。。。
    回归正题。。。
    首先“拍平”就是把字树的中序遍历搞出来,然后暴力重建。。。好暴力
    反正“可以证明,替罪羊树每次重构的复杂度都是均摊O(logn)的”,我不会证明。。。


    Code

    // It is made by XZZ
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    #define rep(a,b,c) for(rg int a=b;a<=c;a++)
    #define drep(a,b,c) for(rg int a=b;a>=c;a--)
    #define erep(a,b) for(rg int a=fir[b];a;a=nxt[a])
    #define il inline
    #define rg register
    #define vd void
    typedef long long ll;
    il int gi(){
        rg int x=0,f=1;rg char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
        return x*f;
    }
    const int maxn=1e6+2;
    const double alpha=0.7777777;
    int root,id,val[maxn],ch[maxn][2],siz[maxn],cover[maxn],b[maxn];
    bool del[maxn];
    il int newnode(int _val){val[++id]=_val,ch[id][0]=ch[id][1]=0,siz[id]=cover[id]=1;return id;}
    il vd dfs(const int&rt){
        if(!rt)return;
        dfs(ch[rt][0]);
        if(!del[rt])b[++b[0]]=rt;
        dfs(ch[rt][1]);
    }
    il int divide(int l,int r){
        if(l>r)return 0;
        int mid=(l+r)>>1;
        ch[b[mid]][0]=divide(l,mid-1);
        ch[b[mid]][1]=divide(mid+1,r);
        siz[b[mid]]=siz[ch[b[mid]][0]]+siz[ch[b[mid]][1]]+!del[b[mid]];
        cover[b[mid]]=cover[ch[b[mid]][0]]+cover[ch[b[mid]][1]]+1;
        return b[mid];
    }
    il vd rebuild(int&rt){
        b[0]=0;dfs(rt);rt=divide(1,b[0]);
    }
    il int*_Insert(int&rt,const int&num){
        if(!rt){rt=newnode(num);return NULL;}
        ++siz[rt],++cover[rt];
        int*ret=_Insert(ch[rt][num>=val[rt]],num);
        if(max(cover[ch[rt][0]],cover[ch[rt][1]])>alpha*cover[rt])ret=&rt;
        return ret;
    }
    il vd Insert(const int&x){int*ls=_Insert(root,x);if(ls)rebuild(*ls);}
    il int Rank(const int&x){
        int ret=1,now=root;
        while(now)
    	if(x<=val[now])now=ch[now][0];
    	else ret+=siz[ch[now][0]]+!del[now],now=ch[now][1];
        return ret;
    }
    il int Kth(int k){
        int now=root;
        while(now){
    	if(!del[now]&&k==siz[ch[now][0]]+1)return val[now];
    	if(k<=siz[ch[now][0]])now=ch[now][0];
    	else k-=siz[ch[now][0]]+!del[now],now=ch[now][1];
        }
    }
    il vd _Erase(int k){
        int now=root;
        while(now){
    	--siz[now];
    	if(!del[now]&&k==siz[ch[now][0]]+1){del[now]=1;return;}
    	if(k<=siz[ch[now][0]])now=ch[now][0];
    	else k-=siz[ch[now][0]]+!del[now],now=ch[now][1];
        }
    }
    il vd Erase(const int&x){
        _Erase(Rank(x));
        if(siz[root]<cover[root]*alpha)rebuild(root);
    }
    int main(){
        int n=gi(),opt,x;
        while(n--){
    	opt=gi(),x=gi();
    	if(opt==1)Insert(x);
    	else if(opt==2)Erase(x);
    	else if(opt==3)printf("%d
    ",Rank(x));
    	else if(opt==4)printf("%d
    ",Kth(x));
    	else if(opt==5)printf("%d
    ",Kth(Rank(x)-1));
    	else printf("%d
    ",Kth(Rank(x+1)));
        }
        return 0;
    }
    
  • 相关阅读:
    微信红包实现算法
    Java中Redis简单入门
    Java之英格玛简单实现以及加密验证码的应用
    Java实现二维码
    String,StringBuffer,StringBuilder个人认为较重要的区别
    Java中Comparable和Comparator你知多少?
    Oracle
    maven下载及安装最详解
    破解路由器
    Activiti工作流学习-----基于5.19.0版本(8)
  • 原文地址:https://www.cnblogs.com/xzz_233/p/7520277.html
Copyright © 2011-2022 走看看