zoukankan      html  css  js  c++  java
  • luoguP3369[模板]普通平衡树(Treap/SBT) 题解

    链接一下题目:luoguP3369[模板]普通平衡树(Treap/SBT)

    平衡树解析

    #include<iostream>
    #include<cstdlib>
    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<iomanip>
    #include<algorithm>
    #include<ctime>
    #include<queue>
    #include<stack>
    #define lst long long
    #define rg register
    #define N 500050
    #define Inf 2147483647
    using namespace std;
    
    int use,root,tot;//操作数,根节点编号,树中元素总和
    struct T{
        int cnt;//这个数相等的数的数量
        int size;//这棵子树上一共有几个元素
        int fa,v;//父亲节点,当前点的权值
        int ch[2];//左(0)右(1)孩子
    }ljl[N];//平衡树
    
    inline int read()
    {
        rg int s=0,m=1;char ch=getchar();
        while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
        if(ch=='-')m=-1,ch=getchar();
        while(ch>='0'&&ch<='9')s=(s<<3)+(s<<1)+ch-'0',ch=getchar();
        return s*m;
    }
    
    inline void Pushup(rg int now)//更新节点size的操作
    {
        ljl[now].size=ljl[ljl[now].ch[0]].size+ljl[ljl[now].ch[1]].size+ljl[now].cnt;
        //当前节点的size是左孩子子树的size加上右孩子子树的size加上本节点相等的数的数量
    }
    
    inline void rotate(rg int x)//把x往上转
    {//定义:x的相对位置为x属于y的?孩子
        rg int y=ljl[x].fa;//父亲
        rg int z=ljl[y].fa;//祖父
        rg int k=ljl[y].ch[1]==x;//x的相对位置
        ljl[z].ch[ljl[z].ch[1]==y]=x;//把x转到y的位置上去
        ljl[x].fa=z;//x的爸爸变成了z
        ljl[y].ch[k]=ljl[x].ch[k^1];//y的x的相对位置的那个孩子变成x的x的相对位置的另一个孩子
        ljl[ljl[x].ch[k^1]].fa=y;//……的爸爸变成y
        ljl[x].ch[k^1]=y;//x的相对位置的另一个孩子变成y
        ljl[y].fa=x;//y的爸爸变成x
        Pushup(x),Pushup(y);//更新一下节点数量
    }
    
    inline void splay(rg int x,rg int goal)//把x转到goal下面,如果goal=0,那么就是转到根节点
    {
        while(ljl[x].fa!=goal)//如果x的父亲不是goal,目标没有达成,就要继续转
        {
            rg int y=ljl[x].fa;//父亲
            rg int z=ljl[y].fa;//祖父
            if(z!=goal)//如果z存在的话
            {
                (x==ljl[y].ch[0])^(y==ljl[z].ch[0])?rotate(x):rotate(y);
                //如果x和y分别是y和z的同一孩子,就把y往上转
                //如果x和y分别是y和z的不同孩子,就把x往上转
            }
            rotate(x);//最后一定会要把x在网上转一次
        }
        if(!goal)root=x;//更新根节点
    }
    
    void Insert(rg int x)//插入x
    {
        rg int now=root,fa=0;//从根开始找,根的父亲是0
        while(ljl[now].v!=x&&now)//只要还没有找到这个数字,且当前这个位置有数,就继续找
        {
            fa=now;//爸爸变成现在的节点
            now=ljl[now].ch[x>ljl[now].v];//如果x比now大,就找now的右孩子,小则左孩子
        }
        if(now)ljl[now].cnt++;//如果存值的位置存在,就直接在计数器上加1
        else//否则
        {
            now=++tot;//增加一个新位置
            if(fa)ljl[fa].ch[x>ljl[fa].v]=now;//如果父亲存在(我不是根),那我的父亲的儿子是我
            ljl[now].v=x;//权值
            ljl[now].fa=fa;//父亲
            ljl[now].cnt=1;//计数器
            ljl[now].size=1;//子树大小
            ljl[now].ch[0]=ljl[now].ch[1]=0;//没有孩子
        }
        splay(now,0);//把当前位置转到根节点,以维持树的平衡
    }
    
    inline void find(rg int x)//找x的位置,把它转到根节点,方便之后的计算
    {
        rg int now=root;//从根开始找
        if(!root)return;//如果是空树,还找个屁
        while(x!=ljl[now].v&&ljl[now].ch[x>ljl[now].v])//如果还没有找到那个点,且我还有符合的儿子
            now=ljl[now].ch[x>ljl[now].v];//就跳转到我的儿子继续找
        splay(now,0);//转到树根去
    }
    
    inline int Next(rg int x,rg int f)//找x的前驱(0)后继(1)
    {
        find(x);//先找到x的位置,可能树顶不是x,是和x值接近的那个元素
        int now=root;//从根开始找
        if(ljl[now].v>x&&f)return now;//如果大于x且我们要找后继,那就是他了
        if(ljl[now].v<x&&!f)return now;//如果小于x且我们要找前驱,那就是他了
        now=ljl[now].ch[f];//那我们从符合条件的儿子开始跳
        while(ljl[now].ch[f^1])now=ljl[now].ch[f^1];//不断的往最优的方向跳
        return now;//返回位置
    }
    
    inline void Delete(rg int x)//删掉x
    {
        rg int qq=Next(x,0);//找到前驱
        rg int hj=Next(x,1);//找到后继
        splay(qq,0),splay(hj,qq);//把前驱转到树根,把后继转到前驱下面
        int del=ljl[hj].ch[0];//那么x就是后继的左儿子
        if(ljl[del].cnt>1)//如果x的计数器大于1
        {
            ljl[del].cnt--;//让x的计数器--
            splay(del,0);//转到树根保持平衡
        }
        else ljl[hj].ch[0]=0;//直接删除x
    }
    
    inline int kth(rg int x)//找第x小的数
    {
        rg int now=root;//从根开始找
        if(ljl[now].size<x)return 0;//如果排名都超过总数了…………
        while(1)//嘿嘿,一直找
        {
            rg int ls=ljl[now].ch[0];//左孩子
            if(ljl[ls].size+ljl[now].cnt<x)//如果排名比左孩子总元素数和与我相等的数总和还大
            {
                x-=ljl[ls].size+ljl[now].cnt;//就减去前面的元素数
                now=ljl[now].ch[1];//去右孩子上找这个排名
            }
            else
                if(ljl[ls].size>=x)now=ls;//如果左孩子里包括它
                else return ljl[now].v;//那就在这个点上了,返回
        }
    }
    
    int main()
    {
        use=read();
        Insert(Inf),Insert(-Inf);
        for(rg int i=1;i<=use;++i)
        {
            rg int opt=read(),x=read();
            if(opt==1)Insert(x);
            if(opt==2)Delete(x);
            if(opt==3)find(x),printf("%d
    ",ljl[ljl[root].ch[0]].size);
            if(opt==4)printf("%d
    ",kth(x+1));
            if(opt==5)printf("%d
    ",ljl[Next(x,0)].v);
            if(opt==6)printf("%d
    ",ljl[Next(x,1)].v);
        }
        return 0;
    }




    哪怕人间是炼狱,梦想永远是天堂
    继续走下去吧,理想永远都年轻,花儿一定会再次盛开
  • 相关阅读:
    [SDOI2015]约数个数和
    [POI2007]ZAP-Queries
    fpu栈溢出
    shader 汇编
    sample a texture as a rendertarget
    ID3d11asynchronous
    DEVICE DRAW VERTEX BUFFER TOO SMALL
    模型的一个点显示在原点
    setrendertraget 上下颠倒
    skinned mesh 蜘蛛样
  • 原文地址:https://www.cnblogs.com/cjoierljl/p/8718950.html
Copyright © 2011-2022 走看看