◆平衡树◆ ◇Treap◇ 营业额统计

◆平衡树◆◇Treap◇ 营业额统计

◆题外话◆

才学完线段树的作者深谙树的恶心,然而看到平衡树,作者发现——“回首向来萧瑟处,也无风雨也无晴”…… 老师自信地以为我们学得很好(没错,我得了NOIP的一等奖,虽然并没有什么用 \(^o^)/),然而作者表示二叉排序树没有学好……
Let’s begin!

◆题目◆

Description

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

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

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

Input

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

Output

输出文件仅有一个正整数,即每一天的最小波动值之和。答案保证在int范围内。

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

题目解析

①平衡树的基础

这是一道平衡树的基础题,只考察了平衡树的插入以及查找。那么作者就用一个比较基础的平衡树结构 —— Treap 来解决这道题吧。

②平衡树的建立

由于这道题没有删除元素的操作,树的元素应该是一个连续的链表,用一个一维的结构体数组储存一棵树。给这个结构体命名为 Tree,该数组为 tree[] 。tree[] 的每一个元素都表示树的一个结点,那么就包含它本身的 key 值,以及左右儿子的下标(如果你一定要用指针我也不管你),当然由于作者用 Treap,随机优先级 pri 也必不可少。
那么结构体就是这样的:

#define NIL -1
inline unsigned int RAND() {return (unsigned int)(rand()*rand());}
struct Tree
{
    int pri,key,son[2];
    Tree() {son[0]=son[1]=NIL;}
    void Into(int KEY) {key=KEY;pri=RAND();son[0]=son[1]=NIL;}
}tree[100005];

一开始的结点的左右儿子可能是空,所以宏定义一个 NIL=-1 ,表示空。大家可以看到这里写了一个 RAND() ,这是写代码的一种习惯,建议这样写。因为在 Windows 系统下 rand() 最大才3万多,但是 linux 系统下 rand() 能够达到 INT_MAX 。那么加上一个RAND() 就可以随心所欲地控制rand()了~(int爆炸没关系,用的是无符号整形)

③旋转操作

直接上模板——

void Rotate_Tree(Tree *&x,int d) //旋转:x为根节点,d为旋转方向(1-右)
{
    Tree *y=x->ch[!d];
    x->ch[!d]=y->ch[d];
    y->ch[d]=x;
    x=y;
}

④插入平衡树

Treap 有2个键值——key、pri。key 以二叉排序树的规则维护(左子树<根≤右子树),而 pri 要以二叉堆的规则维护(左子树≤根 且 右子树≤根)。一般方法是按照二叉排序树的方法插入,即插入到叶节点。但是同时要维护pri,这就是要 递归插入 的原因——
判断当前插入的节点的pri是否小于根节点的pri(不符合规则):旋转(唯一不破坏二叉排序树性质的可以维护二叉堆的方法 ヽ〔゚Д゚〕丿)!

void Push_Tree(int &x,int key)
{
    if(x==NIL)
    {
        x=++Size;
        tree[x].Into(key);
        return;
    }
    int lr=(key<=tree[x].key)? 0:1;
    Push_Tree(tree[x].son[lr],key);
    if(tree[x].pri>tree[tree[x].son[lr]].pri)
        rotate(x,!lr);
}

⑤解决问题

根据题目意思,我们每次操作需要找到与当前元素最接近的一个元素,并求差;然后插入到平衡树内。(Isn’t it easy? (o^^o)♪)

◆代码◆

◇AC◇

/*Lucky_Glass*/
#include
#include
#include
#include
#define NIL -1
using namespace std;
inline unsigned int RAND() {return (unsigned int)(rand()*rand());}
struct Tree
{
    int pri,key,son[2];
    Tree() {son[0]=son[1]=NIL;}
    void Into(int KEY) {key=KEY;pri=RAND();son[0]=son[1]=NIL;}
}tree[100005];
int root=-1,Size,solve;
void rotate(int &x,int lr) 
{
    int y=tree[x].son[!lr];
    tree[x].son[!lr]=tree[y].son[lr];
    tree[y].son[lr]=x;
    x=y;
}
void Push_Tree(int &x,int key)
{
    if(x==NIL)
    {
        x=++Size;
        tree[x].Into(key);
        return;
    }
    int lr=(key<=tree[x].key)? 0:1;
    Push_Tree(tree[x].son[lr],key);
    if(tree[x].pri>tree[tree[x].son[lr]].pri)
        rotate(x,!lr);
}
void Solve_Tree(int x,int key)
{
    if(x==NIL) return;
    solve=min(solve,int(fabs(key-tree[x].key)));
    if(!solve) return;
    int lr=(key>tree[x].key)? 1:0;
    Solve_Tree(tree[x].son[lr],key);
}
int main()
{
    srand(20192419);
    int n_ask,sum,key;
    scanf("%d%d",&n_ask,&key);
    sum=key;
    Push_Tree(root,key);
    for(int i=1;iscanf("%d",&key);
        solve=1e8;
        Solve_Tree(root,key);
        sum+=solve;
        Push_Tree(root,key);
    }
    printf("%d\n",sum);
    return 0;
}

你可能感兴趣的:(#平衡树1,-,Treap套路深#)