zoukankan      html  css  js  c++  java
  • 替罪羊树+3369 【模板】普通平衡树

    传送门

    一听是平衡树

    就jio的很难很难

    然而大佬说很简单

    (听的时候确实jio的没有想象中的那么难<大佬讲的好orz)

    然而

    写代码的时候就完全不是了

    足足花了我两个晚上啊

    终于整的差不多明白了

    (但我觉得我记不太住啊qwq)

    (内疚...)

    这是一个很优雅的平衡树!!!

    什么是优雅呢!

    暴力即优雅!!!

    如果在一棵平衡的二叉搜索树内进行查询等操作

    时间就可以稳定在log(n)

    但是每一次的插入节点和删除节点

    都可能会使得这棵树不平衡

    最坏情况就是退化成一条链

    显然我们不想要这种树

    于是各种维护的方法出现了

    大部分的平衡树都是通过旋转来维护平衡的

    但替罪羊树就很厉害了

    一旦发现不平衡的子树

    立马拍扁重建

    这就是替罪羊树的核心:暴力重建

    那么

    这个替罪羊树都支持那些操作呢?!

    以下:(就是板子题中的那些

      插入x

      删除x数(若有多个相同的数,因只删除一个)

      查询x数的排名(排名定义为比当前数小的数的个数+1+1+1。若有多个相同的数,因输出最小的排名)

      查询排名为x的数

      求x的前驱(前驱定义为小于x,且最大的数)

      求x的后继(后继定义为大于x,且最小的数)

    代码小释:(这丑陋的拼音是我懒得改了,骂死我吧

      zuo,you:记录该节点的左右儿子

           x:该节点的值

           tot:有多少个值为x的数

           size,trsize,whsize:size表示以该节点为根的子树内有多少个节点,trsize表示有多少个有效节点(这个后面再讲啦),whsize表示有多少个(也就是子树内所有节点的tot的和)

           fa:该点的父亲

           tf:该点是否有删除标记(这个也会在后面讲啦

    (个人认为自己272行的码风是极好的)

    #include<cstdio>
    #include<cstring>
    
    struct node
    {
        int zuo,you,x,tot,size,trsize,whsize,fa;
        //tot表示x的个数;size表示以该节点为根的子树内有多少个节点
        //trsize表示有多少个有效节点,whsize表示有多少个数(也就是子树内所有节点的tot的和)
        bool tf;//该点是否有删除标记
    } tree[1000010];
    
    int len = 0,n,root = 0;
    int ck[1000010],t = 0;//ck数组为仓库,存下废弃的节点,t是ck数组的大小 
    double alpha = 0.75;
    
    //新建结点
    void build(int x,int y,int fa)//初始化树上编号为y的节点,它的值为x,父亲为fa
    {
        tree[y].zuo = tree[y].you = 0;
        tree[y].fa = fa;
        tree[y].tf = false;
        tree[y].x = x;
        tree[y].tot = tree[y].size = tree[y].trsize = tree[y].whsize = 1;
        //x的个数,子树的大小,有效的的数量,字数内所有点的tot值 均为一  
    }
    
    //神仙函数 
    inline int kk()
    {
        if(t > 0)
            return ck[t--];//假如仓库内有点,就直接用
        else
            return ++len;//否则再创造一个点
    } 
    
    //更新 
    void updata(int x,int y,int z,int k)
    //x为当前操作结点,y是有效节点增加的个数(trsize),z是子树增大的大小(size),k是数的个数增加的个数(whsize) 
    {
        if(!x)//到头了就停止了 
            return; 
        tree[x].trsize += y;
        tree[x].size += z;
        tree[x].whsize += k;
        updata(tree[x].fa,y,z,k);
    }
    
    //找点 
    int find(int x,int now)//now是当前找到的那个点,x是要找到的值 
    {
        if(x < tree[now].x && tree[now].zuo)
            return find(x,tree[now].zuo);
        if(x > tree[now].x && tree[now].you)
            return find(x,tree[now].you);
        return now;
    }
    
    //存数列的辅助结构体,就是子树拍扁后形成的那个数列 
    struct sl
    {
        int x,tot;
    }shulie[1000010];
    int tt;
    
    void dfs_rebuild(int x)//中序遍历,拍扁 
    {
        if(x == 0)//没有子树,结束 
            return;
        dfs_rebuild(tree[x].zuo);//先去左儿子
        if(!tree[x].tf)//假如没有删除标记,就只将他的x和tot加进数组,因为其他东西都没有用
        {
            shulie[++tt].x = tree[x].x;
            shulie[tt].tot = tree[x].tot;
        }
        ck[++t] = x;//仓库,存下废弃的节点
        dfs_rebuild(tree[x].you); //再去右儿子
    } 
    
    //把拍扁的数列变成树 
    int readd(int l,int r,int fa)
    {
        if(l > r)
            return 0;
        int mid = (l + r)>>1;//中点即为子树的根 
        int id = kk();
        tree[id].fa = fa;
        tree[id].tot = shulie[mid].tot;
        tree[id].x = shulie[mid].x;
        tree[id].zuo = readd(l,mid-1,id);
        tree[id].you = readd(mid + 1,r,id);
        tree[id].whsize = tree[tree[id].zuo].whsize + tree[tree[id].you].whsize + shulie[mid].tot;
        tree[id].size = tree[id].trsize = r-l+1;
        tree[id].tf = false;
        return id; 
    }
    
    //重建以x为根的子树
    void rebuild(int x)
    {
        tt = 0;//数组下标
        dfs_rebuild(x);
        if(x == root)
            root = readd(1,tt,0);
        else
        {
            updata(tree[x].fa,-tree[x].size+tree[x].trsize,0,0);
            if(tree[tree[x].fa].zuo == x)
                tree[tree[x].fa].zuo = readd(1,tt,tree[x].fa);
            else
                tree[tree[x].fa].you = readd(1,tt,tree[x].fa);
        }
    }
    
    //重建(重新找根) 看看是否用拍扁 
    void find_rebuild(int now,int x)//now表示现在走到的节点,x表示要一直走到值为x的点 
    {
        if((double)tree[tree[now].zuo].size>(double)tree[now].size*alpha||
        (double)tree[tree[now].you].size>(double)tree[now].size*alpha||
           (double)tree[now].size-(double)tree[now].trsize>(double)tree[now].size*0.4)
        {
            rebuild(now);
            return;
        }
        if(tree[now].x!=x)
            find_rebuild(x<tree[now].x?tree[now].zuo:tree[now].you,x);//继续向下搜索
    }
    //加点 
    void add(int x)
    {
        if(root == 0)
        {
            build(x,root = kk(),0);
            return;
        }
        int p = find(x,root);
        if(x == tree[p].x)
        {
            tree[p].tot++;
            if(tree[p].tf)
            {
                tree[p].tf = false;
                updata(p,1,0,1);
            }
            else
                updata(p,0,0,1);
        }
        else
        if(x < tree[p].x)
        {
            build(x,tree[p].zuo = kk(),p);
            updata(p,1,1,1);
        }
        else
        {
            build(x,tree[p].you = kk(),p);
            updata(p,1,1,1);
        }
        find_rebuild(root,x);
    } 
    
    //删点
    void del(int x)
    {
        int p = find(x,root);
        tree[p].tot--;
        if(!tree[p].tot)
        {
            tree[p].tf = true;
            updata(p,-1,0,-1);
        }
        else
            updata(p,0,0,-1);
        find_rebuild(root,x);
    } 
    
    //查询x数的排名 
    void findxpm(int x)
    {
        int now = root;
        int ans = 0;
        while(tree[now].x != x)
        {
            if(x < tree[now].x)
                now = tree[now].zuo;
            else
            {
                ans += tree[tree[now].zuo].whsize + tree[now].tot;
                now = tree[now].you;
            }
        }
        ans += tree[tree[now].zuo].whsize;
        printf("%d
    ",ans+1);
    }
    
    //查询排名为x的数 
    void findpmx(int x)
    {
        int now=root;
        while(1)
        {
            if(x<=tree[tree[now].zuo].whsize)
                now=tree[now].zuo;
            else
            {
                x-=tree[tree[now].zuo].whsize;
                if(x<=tree[now].tot)
                {
                    printf("%d
    ",tree[now].x);
                    return;
                }
                x-=tree[now].tot;
                now=tree[now].you;
            }
        }
    }
    
    //求x的前驱
    int pre(int now,int x,bool zy)
    {
        if(!zy)
            return pre(tree[now].fa,x,tree[tree[now].fa].zuo!=now);
        if(!tree[now].tf&&tree[now].x<x)
            return tree[now].x;
        if(tree[now].zuo)
        {
            now=tree[now].zuo;
            while(tree[now].you)
                now=tree[now].you;
            return tree[now].x;
        }
        return pre(tree[now].fa,x,tree[tree[now].fa].zuo!=now);
    }
    
    //求x的后继 
    int nxt(int now,int x,bool zy)
    {
        if(!zy)
            return nxt(tree[now].fa,x,tree[tree[now].fa].you!=now);
        if(!tree[now].tf&&tree[now].x>x)
            return tree[now].x;
        if(tree[now].you)
        {
            now=tree[now].you;
            while(tree[now].zuo)
                now=tree[now].zuo;
            return tree[now].x;
        }
        return nxt(tree[now].fa,x,tree[tree[now].fa].you!=now);
    }
    
    int main()
    {
        scanf("%d",&n);
        while(n--)
        {
            int id,x;
            scanf("%d %d",&id,&x);
            if(id==1)
                add(x);
            if(id==2)
                del(x);
            if(id==3)
                findxpm(x);
            if(id==4)
                findpmx(x);
            if(id==5)
                printf("%d
    ",pre(find(x,root),x,true));
            if(id==6)
                printf("%d
    ",nxt(find(x,root),x,true));
        }
        return 0; 
    }
  • 相关阅读:
    逆向
    BUUCTF
    学校健康系统自动打卡
    SQL数据库操作练习(3)
    简单尝试UPX脱壳
    网站WAF-安全狗的绕过(一)
    【题解】担心
    【题解】树上的鼠
    【题解】CF1299B Aerodynamic
    【题解】等你哈苏德
  • 原文地址:https://www.cnblogs.com/darlingroot/p/10480537.html
Copyright © 2011-2022 走看看