BZOJ-1588: [HNOI2002]营业额统计 (set查找前驱后继 splay练习题)

1588: [HNOI2002]营业额统计

Time Limit: 5 Sec   Memory Limit: 162 MB
Submit: 17311   Solved: 7036
[ Submit][ Status][ Discuss]

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

#include 
using namespace std;
#define maxn 100005
int pre[maxn], ch[maxn][2], key[maxn], rt, tot;
void newNode(int& r, int fa, int val){
    r = ++tot;
    pre[r] = fa;
    key[r] = val;
    ch[r][0] = ch[r][1] = 0;
}
void rotate(int r, int kind){
    int y = pre[r];
    ch[y][!kind] = ch[r][kind];
    pre[ch[r][kind]] = y;
    if(pre[y]){
        ch[pre[y]][ch[pre[y]][1] == y] = r;
    }
    ch[r][kind] = y;
    pre[r] = pre[y];
    pre[y] = r;
}
void splay(int r, int goal){
    while(pre[r] != goal){
        if(pre[pre[r]] == goal){
            rotate(r, ch[pre[r]][0] == r);
        }
        else{
            int y = pre[r];
            int kind = ch[pre[y]][0] == y;
            if(ch[pre[r]][!kind] == r){
                rotate(y, kind);
                rotate(r, kind);
            }
            else{
                rotate(r, !kind);
                rotate(r, kind);
            }
        }
    }
    if(goal == 0) rt = r;
}
int insert(int val){
    int r = rt;
    while(1){
        if(val < key[r]){
            if(ch[r][0]) r = ch[r][0];
            else break;
        }
        else if(val > key[r]){
            if(ch[r][1]) r = ch[r][1];
            else break;
        }
        else return 0;
    }
    newNode(ch[r][val > key[r]], r, val);
    splay(ch[r][val > key[r]], 0);
    return 1;
}
int getmin(int val){
    int r = rt;
    if(ch[r][0] == 0) return 1 << 29;
    r = ch[r][0];
    while(ch[r][1]) r = ch[r][1];
    return key[r];
}
int getmax(int val){
    int r = rt;
    r = ch[r][1];
    if(r == 0) return 1 << 29;
    while(ch[r][0]) r = ch[r][0];
    return key[r];
}
int main(){
    int n, x;
    scanf("%d", &n);
    int ans = 0;
    rt = tot = 0;
    for(int i = 1; i <= n; ++i){
        scanf("%d", &x);
        if(rt == 0){
            newNode(rt, 0, x);
            ans += x;
        }
        else{
            if(insert(x) == 0){
                continue;
            }
            else{
                int l = getmin(x);
                int r = getmax(x);
                ans += min(abs(l - x), abs(r - x));
            }
        }
    }
    printf("%d\n", ans);
}

/*
题意:4e4天,每天有一个营业额,找到当天之前营业额与当天营业额差值最小的一天,然后累加这个差值。

思路:
这题可以用set来维护前缀,然后用lowerbound和upperbound来找。作为splay的模板题也是不错的。
注意第一天差值就是第一天的营业额。后面如果插入的值已经存在,就不能重复插入,然后找前驱和后继,
简单的在splay上dfs即可。
*/


你可能感兴趣的:(数据结构-Splay)