[HNOI2002]营业额统计

转载自大神:   http://blog.csdn.net/ACM_cxlove?viewmode=contents

营业额统计

Time Limit:5000MS    Memory Limit:165888KB    64bit IO Format:%lld & %llu

Description

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

Input

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

Output

输出文件仅有一个正整数,即Sigma(每天最小的波动值) 。结果小于2^31 。

Sample Input

	6
5
1
2
5
4
6 

Sample Output

	12

看大神的题解半天都没有看懂,最后发现是题意没理解,这不就是每次输入数字时,在已有数字钟计算与此数字之差绝对值,将最小的绝对值加上。


大神的注释写的较简略,我最近加了一些。

代码:

#include <algorithm>
#include <iostream>
#include <sstream>
#include <cstring>
#include <cstdlib>
#include <string>
#include <vector>
#include <cstdio>
#include <stack>
#include <cmath>
#include <queue>
#include <map>
#include <set>
using namespace std;
#define N 100005
#define INF 0x3f3f3f3f;
int pre[N],key[N],ch[N][2],root,tot1;//分别表示父节点,键值,左右孩子(0为左孩子,1为右孩子),根节点,节点个数
int n;
//新建一个节点
void newnode(int &r,int father,int k){
    r=++tot1;
    pre[r]=father;
    key[r]=k;
    ch[r][0]=ch[r][1]=0;//新建节点孩子为空
}
//旋转,kind为1表示右旋,kind为0表示左旋
void rotate(int x,int kind){
    int y=pre[x];
    //将当前节点x的某一分支分给父节点,如x为y的左子节点,此处kind就应为1表示右旋,那么将x的右分支交给y,当做y的左分支
    ch[y][!kind]=ch[x][kind];
    pre[ch[x][kind]]=y;
    //如果父节点不是根节点,就要将当前节点与父节点的父节点连接起来
    if (pre[y]) {
        if (ch[pre[y]][1]==y)
            ch[pre[y]][1]=x;
        else
            ch[pre[y]][0]=x;
    }
    pre[x]=pre[y];
    //旋转后x的旋转方向的子节点即为原来的父节点
    ch[x][kind]=y;
    pre[y]=x;
}
//splay调整,将根为r的子树调整为goal
void splay(int r,int goal){
    while (pre[r]!=goal) {
        //父节点即是目标位置,goal为0表示该点的父节点就是根节点
        if (pre[pre[r]]==goal) {
            //下方原理与rotate函数中解释相同,a为b左子节点,就应该右旋。
            rotate(r,ch[pre[r]][0]==r);
        }
        else {
            int y=pre[r];
            int kind=ch[pre[y]][0]==y;
            //若两个方向不同,则先左旋再右旋(这句话以y为x的左子节点,r为y的右节点为例)
            if (ch[y][kind]==r) {
                rotate(r, !kind);
                rotate(r, kind);
            }
            //若两个方向相同,则相同方向连续两次
            else {
                rotate(y, kind);
                rotate(r, kind);
            }
        }
    }
    //父节点变为根节点时,更新一下根节点
    if (goal==0) {
        root=r;
    }
}

int insert(int k){
    int r=root;
    while (ch[r][key[r]<k]) {//此循环主要是通过二分查找的形式找到与k值相对应的父节点r
        //不重复插入
        if (key[r]==k) {
            splay(r, 0);
            return 0;
        }
        if (key[r]<k)
            r=ch[r][1];
        else
            r=ch[r][0];
    }
    if (key[r]<k) {
        newnode(ch[r][1], r, k);
         //将新插入的节点更新至根节点,下同理
        splay(ch[r][1], 0);
    }
    else {
        newnode(ch[r][0], r, k);
        splay(ch[r][0], 0);
    }
    return 1;
}
//找前驱,即左子树的最右结点
int get_pre(int x){
    int temp=ch[x][0];
    if (temp==0)
        return INF;
    while (ch[temp][1]) {
        temp=ch[temp][1];
    }
    return key[x]-key[temp];
}
//找后继,即右子树的最左结点
int get_next(int x){
    int temp=ch[x][1];
    if (temp==0)
        return INF;
    while (ch[temp][0]) {
        temp=ch[temp][0];
    }
    return key[temp]-key[x];
}

int main(){
    while (scanf("%d",&n)!=EOF) {
        root=tot1=0;
        int ans=0;
        for (int i=1; i<=n; i++) {
            int num;
            if (scanf("%d",&num)==EOF) {
                num=0;
            }
            if (i==1) {
                ans+=num;
                newnode(root, 0, num);
                continue;
            }
            if (insert(num)==0) {
                continue;
            }
            int a=get_next(root);
            int b=get_pre(root);
            ans+=min(a,b);
        }
        printf("%d\n",ans);
    }
    return 0;
}


你可能感兴趣的:([HNOI2002]营业额统计)