BZOJ 1588: [HNOI2002]营业额统计 (Splay入门题目)

本文介绍了一个营业额波动分析的问题,通过使用Splay树(伸展树)来高效计算每天的最小波动值,从而评估公司营业稳定性。Splay树在插入新数时保持平衡,确保快速查询和更新。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description
营业额统计 Tiger最近被公司升任为营业部经理,他上任后接受公司交给的第一项任务便是统计并分析公司成立以来的营业情况。 Tiger拿出了公司的账本,账本上记录了公司成立以来每天的营业额。分析营业情况是一项相当复杂的工作。由于节假日,大减价或者是其他情况的时候,营业额会出现一定的波动,当然一定的波动是能够接受的,但是在某些时候营业额突变得很高或是很低,这就证明公司此时的经营状况出现了问题。经济管理学上定义了一种最小波动值来衡量这种情况: 该天的最小波动值 当最小波动值越大时,就说明营业情况越不稳定。 而分析整个公司的从成立到现在营业情况是否稳定,只需要把每一天的最小波动值加起来就可以了。你的任务就是编写一个程序帮助Tiger来计算这一个值。 第一天的最小波动值为第一天的营业额。  输入输出要求

Input
第一行为正整数 ,表示该公司从成立一直到现在的天数,接下来的n行每行有一个整数(有可能有负数) ,表示第i
天公司的营业额。
天数n<=32767,
每天的营业额ai <= 1,000,000。
最后结果T<=2^31
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

该题数据bug已修复.----2016.5.15

题目很简单,甚至直接用set就能搞过去。但是为了学习下splay,所以做了这个题目

Splay就是一种伸展树,每次插入一个新数,都会把这个数扭到根的位置,而且保持着左边比他小,右边比他大的,二叉搜索树的性质。因为普通的二叉搜索树,在有序的数输入下,会退化为一条链,所以有了splay我们可以保持树的相对平衡,使得每次插入,查询等操作的复杂度都是O(logN)的。
对于这个题目,我们只需要每次把数插进入,寻找左子树中最右边的节点,和右子树种最左边的即可。注意查询不到的情况。

代码如下:

#include<bits/stdc++.h>

using namespace std;
const int MAX = 40010*4;
const int INF = 0x3f3f3f3f;
int ch[MAX][2],f[MAX],key[MAX];
int root,sz;
typedef long long ll;
inline int get(int x){
    return ch[f[x]][1] == x;
}
inline void Rotate(int x){
    int y = f[x],z = f[y];
    int kind = get(x);
    ch[y][kind] = ch[x][!kind];
    f[ch[y][kind]] = y;
    f[y] = x;ch[x][!kind] = y;
    f[x] = z;
    if(z)   ch[z][ch[z][1] == y] = x;
}
inline void Splay(int x){
    for(int fa;(fa = f[x]);Rotate(x)){
        if(f[fa])
            Rotate((get(x)==get(fa))? fa:x);
    }
    root = x;
}
inline bool Insert(int x){
    if(root == 0){
        sz++;
        ch[sz][0] = ch[sz][1] = f[sz] = 0;
        root = sz;key[sz] = x;
        return true;
    }
    int now = root,fa = 0;
    while(true){
        if(key[now] == x){
            Splay(now);
            return false;
        }
        fa = now;
        now = ch[now][x > key[now]];
        if(now == 0){
            sz++;
            ch[sz][0] = ch[sz][1] = 0;
            root = sz;key[sz] = x;
            f[sz] = fa;
            ch[fa][x > key[fa]] = sz;
            Splay(sz);
            return true;
        }
    }
}
int t1,t2;
void QueryPre(){
    int k = ch[root][0];
    if(!k)  return;
    while(ch[k][1]) k = ch[k][1];
    t1 = key[k];
}
void QueryNex(){
    int k = ch[root][1];
    if(!k)  return;
    while(ch[k][0]) k = ch[k][0];
    t2 = key[k];
}
int main(void){
    int N,x;
    ll res = 0;
    scanf("%d",&N);
    for(int i=1;i<=N;++i){
        scanf("%d",&x);
        if(!Insert(x))  continue;
        if(i == 1)  res += x;//第一个数,查不到前驱和后继。
        else{
            t1 = -INF,t2 = INF;
            QueryPre();
            QueryNex();
            //printf("%d %d\n",t1,t2);
            res += min(x-t1,t2-x);
        }
    }
    printf("%lld\n",res);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值