BZOJ 1588 营业额统计 (Splay入门题 插入 找前驱后继)

题目链接

BZOJ1588

题目大意

输入 n 个数(n32767),每读入一个数,在前面输入的数中找到一个与该数相差最小的一个。把所有的差值加起来得到答案。

分析

我写的第一道Splay的题,基本就是对着大神的模板写。
每读入一个数就插入到伸展树中,然后将它旋转到根结点。跟它相差最小的点就是此时左子树中的最右边点和右子树中最左边点,映射到线性有序数组就是它的前驱结点和后驱结点。

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#define ls (rt<<1)
#define rs (rt<<1|1)
using namespace std;
const double pi=4*atan(1.0);
const int MAXN=100010;
const int MAXM=2*MAXN;

struct SplayTree
{
    int root,tot;///根节点、结点数量
    int nt[MAXN][2],pre[MAXN],val[MAXN];
    ///分别表示左右孩子(0左1右)、父节点、键值
    void Init() {root=tot=0;}
    void Newnode(int &rt,int father,int value) ///新建一个结点
    {
        rt=++tot;
        nt[rt][0]=nt[rt][1]=0;
        pre[rt]=father;
        val[rt]=value;
    }
    void Rotate(int x,int kind)///旋转:kind 0为左旋,1为右旋
    {
        int y=pre[x];
        nt[y][!kind]=nt[x][kind];
        pre[nt[x][kind]]=y;
        if (pre[y])
        {
            nt[pre[y]][nt[pre[y]][1]==y]=x;
        }
        pre[x]=pre[y];
        nt[x][kind]=y;
        pre[y]=x;
    }
    void Splay(int x,int goal)///Splay调整:将结点x调整到goal下面
    {
        while (pre[x]!=goal)
        {
            if (pre[pre[x]]==goal)            ///单旋情况
                Rotate(x,nt[pre[x]][0]==x);
            else
            {
                int y=pre[x];
                int kind=nt[pre[y]][0]==y;
                if (nt[y][kind]==x)   ///之字型旋转
                {
                    Rotate(x,!kind);
                    Rotate(x,kind);
                }
                else                 ///一字型旋转
                {
                    Rotate(y,kind);
                    Rotate(x,kind);
                }
            }
        }
        if (!goal) root=x;
    }
    bool Insert(int k)
    {
        int rt=root;
        while (nt[rt][val[rt]<k])
        {
            if (val[rt]==k)///如果本身存在这个元素,只需把它调整到根节点即可
            {
                Splay(rt,0);
                return false;
            }
            rt=nt[rt][val[rt]<k];
        }
        Newnode(nt[rt][val[rt]<k],rt,k); ///不存在这个元素就新插入这个元素
        Splay(nt[rt][val[rt]<k],0);
        return true;
    }
    int get_pre(int rt)///找rt结点的前驱结点 即左子树中最右边的结点
    {
        int x=nt[rt][0];
        while (x)
        {
            while (nt[x][1])
                x=nt[x][1];
            return val[x];
        }
        return -1;
    }
    int get_next(int rt)///找rt结点的后继结点 即右子树中最左边的结点
    {
        int x=nt[rt][1];
        while (x)
        {
            while (nt[x][0])
                x=nt[x][0];
            return val[x];
        }
        return -1;
    }
};
SplayTree tree;
int main()
{
    ios::sync_with_stdio(false);
    int i,ans,num,n;
    //freopen("in.txt","r",stdin);
    while (scanf("%d",&n)!=EOF)
    {
        tree.Init();
        ans=0;
        for (i=1;i<=n;i++)
        {
            scanf("%d",&num);
            if (i==1)
            {
                ans+=num;
                tree.Newnode(tree.root,0,num);
                continue;
            }
            if (!tree.Insert(num)) continue;
            int a=tree.get_pre(tree.root);
            int b=tree.get_next(tree.root);
            if (a==-1) ans+=abs(num-b);   ///要注意前继
            else if (b==-1) ans+=abs(num-a);
            else
                ans+=min(abs(num-b),abs(num-a));
        }
        printf("%d\n",ans);
    }
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值