BZOJ1588: [HNOI2002]营业额统计

Description
Tiger最近被公司升任为营业部经理,他上任后接受公司交给的第一项任务便是统计并分析公司成立以来的营业情况。
Tiger拿出了公司的账本,账本上记录了公司成立以来每天的营业额。分析营业情况是一项相当复杂的工作。由于节假日,大减价或者是其他情况的时候,营业额会出现一定的波动,当然一定的波动是能够接受的,但是在某些时候营业额突变得很高或是很低,这就证明公司此时的经营状况出现了问题。经济管理学上定义了一种最小波动值来衡量这种情况:
该天的最小波动值=min{|该天以前某一天的营业额-该天营业额|}。
当最小波动值越大时,就说明营业情况越不稳定。而分析整个公司的从成立到现在营业情况是否稳定,只需要把每一天的最小波动值加起来就可以了。你的任务就是编写一个程序帮助Tiger来计算这一个值。
第一天的最小波动值为第一天的营业额。
Input
第一行为正整数 ,表示该公司从成立一直到现在的天数,接下来的n行每行有一个整数(有可能有负数) ,表示第i天公司的营业额。
Output
输出文件仅有一个正整数,即Sigma(每天最小的波动值) 。结果小于2^31 。
Sample Input
6
5
1
2
5
4
6
Sample Output
12
HINT
结果说明:5+|1-5|+|2-1|+|5-5|+|4-5|+|6-5|=5+4+1+0+1+1=12
此题数据有问题,详见讨论版http://www.lydsy.com/JudgeOnline/wttl/wttl.php?pid=1588
Source

教训:千万不要跟数据过不去,本题数据好像是少一行还是怎么样的,读入时必须这样:if(scanf("%d",&x)==EOF) x=0;
在COGS上提交了N遍,每次都T的我几乎卡评测了,换到BZOJ上交,A了。。。
此题做法相当多,基本上二叉排序树的各种高级数据结构都能过(splay、AVL、红黑树、SBT等),我写了一个SBT模板。
此题可以理解为在前几个数中找一个与当前数最接近的,那么就构建二叉排序树,从根结点开始,假设当前为s节点,若key[s]>now,那就去左子树中继续找(右子树中的key都大于key[s],所以右子树中一定没有最佳答案),反之就去右子树中找。

#include<cstdio>
#include<algorithm>
#define INF 2147483647
#define isnum(t) ((t>=48)and(t<=57))
using namespace std;
const int N=100010;
int n,now,ans,root;
int key[N],left[N],right[N],size[N];
void in(int &x)
{
    char t=getchar();int f=1;x=0;
    while(!isnum(t)){if(t=='-')f=-1;t=getchar();}
    while(isnum(t)){x=x*10+t-48;t=getchar();}
    x*=f;
}
void leftrot(int &t)
{
    int y=right[t];
    right[t]=left[y];
    left[y]=t;
    size[y]=size[t];
    size[t]=size[left[t]]+size[right[t]]+1;
    t=y;
}
void rightrot(int &t)
{
    int y=left[t];
    left[t]=right[y];
    right[y]=t;
    size[y]=size[t];
    size[t]=size[left[t]]+size[right[t]]+1;
    t=y;
}
void maintain(int &t,bool flag)
{
    if (!flag)
    {
        if (size[left[left[t]]]>size[right[t]])  rightrot(t);
        else
        if (size[right[left[t]]]>size[right[t]])
        {leftrot(left[t]);rightrot(t);}
        else return;
    }
    else
    {
        if (size[right[right[t]]]>size[left[t]]) leftrot(t);
        else
        if (size[left[right[t]]]>size[left[t]])
        {rightrot(right[t]);leftrot(t);}
        else return;
    }
    maintain(left[t],0);
    maintain(right[t],1);
    maintain(t,0);
    maintain(t,1);
}
void sinsert(int &t,int data)
{
    if (t==0)
    {
        t=++now;
        size[t]=1;left[t]=right[t]=0;
        key[t]=data;return;
    }
    ++size[t];
    if (data<key[t]) sinsert(left[t],data);
    else sinsert(right[t],data);
    maintain(t,data>=key[t]);
}
int jisuan(int keynow)
{
    int s=root,o=INF;
    while(s)
    {
        o=min(o,abs(key[s]-keynow));
        if (key[s]>keynow) s=left[s];
        else s=right[s];
    }
    return o;
}
int main()
{
    in(n);int x;in(x);ans+=x;
    sinsert(root,x);
    for (int i=2;i<=n;++i)
    {
        if(scanf("%d",&x)==EOF) x=0;
        ans+=jisuan(x);
        sinsert(root,x);
    }
    printf("%d",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值