HNOI2002 营业额统计

codevs1296 营业额统计

http://codevs.cn/problem/1296/

2002年

 题目等级 : 大师 Master
 
题目描述 Description

Tiger最近被公司升任为营业部经理,他上任后接受公司交给的第一项任务便是统计并分析公司成立以来的营业情况。

Tiger拿出了公司的账本,账本上记录了公司成立以来每天的营业额。分析营业情况是一项相当复杂的工作。由于节假日,大减价或者是其他情况的时候,营业额会出现一定的波动,当然一定的波动是能够接受的,但是在某些时候营业额突变得很高或是很低,这就证明公司此时的经营状况出现了问题。经济管理学上定义了一种最小波动值来衡量这种情况:

该天的最小波动值 = min{|该天以前某一天的营业额-该天营业额|}

当最小波动值越大时,就说明营业情况越不稳定。

而分析整个公司的从成立到现在营业情况是否稳定,只需要把每一天的最小波动值加起来就可以了。你的任务就是编写一个程序帮助Tiger来计算这一个值。

第一天的最小波动值为第一天的营业额。

输入描述 Input Description

第一行为正整数n(n<=32767),表示该公司从成立一直到现在的天数,接下来的n行每行有一个正整数ai(ai<=1000000),表示第i天公司的营业额。

输出描述 Output Description

输出文件仅有一个正整数,即每天最小波动值之和,小于231

样例输入 Sample Input

6

5

1

2

5

4

6

样例输出 Sample Output

12

数据范围及提示 Data Size & Hint

结果说明:5+|1-5|+|2-1|+|5-5|+|4-5|+|6-5|=5+4+1+0+1+1=12

splay模板题,用到了插入、伸展、查询前驱、后继的操作

splay的讲解网上一搜一大坨,这里就不写了

注意营业额可能为负

#include<cstdio>
#include<algorithm>
#define N 32770
#define INF 2000000
using namespace std;
int root,ans;
class splay_tree
{
    private:
        int pre[N],ch[N][2],key[N],tot;
    public:
        inline void newnode(int &r,int fa,int k)
        {
            r=++tot;
            pre[r]=fa;
            key[r]=k;
        }
        inline void rot(int x,int kind)
        {
            int y=pre[x];
            ch[y][!kind]=ch[x][kind];
            pre[ch[x][kind]]=y;
            ch[x][kind]=y;
            if(pre[y]) ch[pre[y]][ch[pre[y]][1]==y]=x;
            pre[x]=pre[y];
            pre[y]=x;
        }
        inline void splay(int x,int goal)
        {            
            while(pre[x]!=goal)
            {
                int y=pre[x];
                int kind=ch[y][0]==x;
                if(pre[y]==goal) rot(x,kind);
                else
                {
                    kind=ch[pre[y]][0]==y;
                    if(ch[y][!kind]==x)
                    {                        
                        rot(y,kind);
                        rot(x,kind);
                    }
                    else
                    {
                        rot(x,!kind);
                        rot(x,kind);
                    }
                }
            }
            root=x;
        }
        inline int insert(int x)
        {
            int r=root;
            while(ch[r][x>key[r]]) 
            {
                if(x==key[r]) {splay(r,0);return 0;}
                r=ch[r][x>key[r]];
            }
            if(x==key[r]) {splay(r,0);return 0;}
            newnode(ch[r][x>key[r]],r,x);
            splay(ch[r][x>key[r]],0);
            return 1;
        }
        inline int get_pre(int x)
        {
            int r=ch[x][0];
            if(!r) return -INF;
            while(ch[r][1]) r=ch[r][1];
            return key[r];
        }
        inline int get_suf(int x)
        {
            int r=ch[x][1];
            if(!r) return INF;
            while(ch[r][0]) r=ch[r][0];
            return key[r];
        }
        
}tree;
int main()
{
    int n,x;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&x);
        if(i==1)
        {
            ans+=x;
            tree.newnode(root,0,x);
        }
        else
        {
            if(!tree.insert(x)) continue;
            int a=tree.get_pre(root);
            int b=tree.get_suf(root);
            ans+=min(x-a,b-x);
        }    
    }
    printf("%d",ans);
}

1、自己写的1个错误:

INF选值问题,开始考虑的是1000000(营业额最大值)

不够大,因为有一个坑:营业额可能为负

 假设有一天的营业额为-900000,没有比它营业额更小的一天,它的后继是200000

那么如果INF选1000000,a=100000,b=1100000,ans+=a

实际ans应+=b

 所以考虑负数,INF应至少选2000000

2、不影响正确性的一个细节

插入原来是这样写的:

while(ch[r][x>key[r]])
{
    if(key[r]==x){splay(r,0);return 0;}
    r=ch[r][key[r]<x];
}    
newnode(ch[r][key[r]<x],r,x);

现在这样写:

while(ch[r][x>key[r]]) 
{
       if(x==key[r]) {splay(r,0);return 0;}
       r=ch[r][x>key[r]];
}
if(x==key[r]) {splay(r,0);return 0;}
newnode(ch[r][x>key[r]],r,x);        

原来那样写以为不会插入相同元素,但实际上是会的

因为只有节点有孩子是才会判断是否相同,

如果上一个x位于叶子节点的位置,判断不出来

所以如果为了防止插入重复元素,要在插入之前再判断一次

这里要感谢良心数据,发现了这个理解误区

posted @ 2017-02-07 09:05  TRTTG  阅读(450)  评论(1编辑  收藏  举报