Spaly详解

P2234 [HNOI2002]营业额统计

链接戳这里☞

营业额统计

上代码(附有注释):

    #include<bits/stdc++.h>
    using namespace std;
    struct sd
    {
        int value;//当前节点所储存的值 
        int son[2];//左右儿子 
        int delta; //暂时没用
        int father;//自己的爸爸 
        int below;//自己下面树的大小 
    }tree[1000005];
    int root;//根 
    int cnt;//下标分配 
    int dir;//旋转方向 0是左边 1是右边 
    inline void maintain(int now)//维护 更新左右子树的大小(这道题中可以不写)
    {
        tree[now].below=1;
        if(tree[now].son[0])tree[now].below+=tree[tree[now].son[0]].below;
        if(tree[now].son[1])tree[now].below+=tree[tree[now].son[1]].below;
    }
    inline int get(int now)//确定旋转方向 
    {
        if(tree[tree[now].father].son[0]==now)return 0;
        return 1;
    }
    //---------------------------------------------------------------------- 
    void rotate(int now)//旋转 需要重点理解 
    {
        dir=get(now);
        int fa=tree[now].father,gra=tree[fa].father;
        tree[fa].son[dir]=tree[now].son[dir^1];
        tree[tree[now].son[dir^1]].father=fa;
        if(gra)tree[gra].son[get(fa)]=now;
        tree[now].father=gra;
        tree[fa].father=now;
        tree[now].son[dir^1]=fa;
        maintain(fa);
        maintain(now);
    }
    //---------------------------------------------------------------------- 
    inline void splay(int now)
    {
        int gra,fat;//定义now的祖父和爸爸 
        int opt1,opt2;//定义两次旋转方向 
        while(tree[now].father)//如果now还有自己的爸爸,即now还没有旋转到根节点。继续! 
        {
            fat=tree[now].father;gra=tree[fat].father;
            if(gra!=0)//如果有祖父,即要进行双旋! 
            {
                opt1=get(now);opt2=get(fat);
                if(opt1==opt2){rotate(fat),rotate(now);}
                else{rotate(now),rotate(now);}
            }
            else
            rotate(now);
        }
        root=now;//更新根节点 
    }
    void insertit(int now,int val,int pre)
    {
        if(!now)//如果now为零(零是假的,其余是真的)说明他没有(左右)儿子,就插入,并且进行根节点旋转
        {
            cnt++;
            tree[cnt].father=pre;
            tree[cnt].value=val;
            tree[cnt].below=1;
            if(tree[pre].value>val)tree[pre].son [0]=cnt;
            else tree[pre].son[1]=cnt;
            splay(cnt);
        }
        else//如果now不是零 
        {
            if(tree[now].value>val)insertit(tree[now].son[0],val,now);
            else insertit(tree[now].son[1],val,now);
        }
    }
    inline void in(int &x)//快读 
    {
        bool fu=false;
        x=0;char c=getchar();
        while(!isdigit(c))
            {
            if(c=='-')
            {
                fu=true;
            }
            c=getchar();}
        while(isdigit(c)){x*=10,x+=c-48;c=getchar();}
        if(fu)x*=-1;
    }
    int find_pre(int now)//查找前驱 
    {
        if(!now)return -99999999;//如果没有左子树了 
        if(tree[now].son[1])return find_pre(tree[now].son[1]);
        //如果左子树下有右子树,就在继续找右子树的右子树 (上面) 
        return tree[now].value;
        //如果没有右子树了,打出当前值 
    }
    int find_suc(int now)//查找后继 
    {
        if(!now)return 99999999;//如果没有右子树了 
        if(tree[now].son[0])return find_suc(tree[now].son[0]);
        //如果右子树下有左子树,就在继续找左子树的左子树 (上面) 
        return tree[now].value;
        //如果没有左子树了,打出当前值 
    }
    int main()
    {
        long long tot=0;
        int num,prev,sucv;
        in(num);
        int a;
        while(num--)
        {
            in(a);
            insertit(root,a,root);//读入(从当前根向下扔数) 
            prev=find_pre(tree[root].son[0]);//找前驱 (比他小) 
            sucv=find_suc(tree[root].son[1]);//找后继(比他大) 
            if(sucv==99999999&&prev==-99999999)//判定是否为第一个结点 
            {
                tot+=a;
                continue;
            }
            if(a-prev>sucv-a)tot+=sucv-a;//从前驱后继中取小 ,更新总和 
            else tot+=a-prev;//更新总和 
        }
        printf("%lld",tot);//返回总和 
        return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值