HNOI2002营业额统计(伸展树)

1588: [HNOI2002]营业额统计

Time Limit: 5 Sec   Memory Limit: 162 MB
Submit: 13727   Solved: 5128
[ Submit][ Status][ Discuss]

Description

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

Input

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

Output

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

Sample Input

6
5
1
2
5
4
6

Sample Output

12

为了完成数据结构课设,简单实现了下伸展树,顺便做道题巩固下

找最小波动值即找树中键值最接近它的节点

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <stack>
#include <bitset>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <algorithm>
#define FOP freopen("data.txt","r",stdin)
#define inf 0x3f3f3f3f
#define maxn 100005
#define mod 1000000007
#define PI acos(-1.0)
#define LL long long
using namespace std;

//父节点
int parent[maxn];

//孩子节点,0表示左孩子,1表示右孩子
int children[maxn][2];

//每个节点的键值
int value[maxn];

//根节点
int root;

//节点总数
int cot;

//新建一个节点
void newNode(int &x, int pre, int val)
{
    x = ++cot;

    //设置父节点
    parent[x] = pre;

    //设置键值
    value[x] = val;

    //左右孩子为空
    children[x][0] = children[x][1] = 0;
}

//旋转,dir为1为右旋,dir为0为左旋
void rotate_tree(int x, int dir)
{
    int y = parent[x];

    //把其中一个分支先给父节点
    children[y][!dir] = children[x][dir];
    parent[children[x][dir]] = y;

    //如果y节点的父节点不是根结点,则要和父节点的父节点连接起来
    if(parent[y] != 0)
    {
        if(children[parent[y]][1] == y)
        {
            children[parent[y]][1] = x;
        }
        else
        {
            children[parent[y]][0] = x;
        }
    }

    //设置x节点的父节点为y的父节点
    parent[x] = parent[y];

    //设置y节点为x节点的分支
    children[x][dir] = y;

    //同步y节点的父节点为x节点
    parent[y] = x;
}

//Splay调整,将x节点调整为goal的子树
void Splay(int x,int goal)
{
    while(parent[x] != goal)
    {
        //父节点即是目标位置,若goal为0表示,父节点就是根结点
        if(parent[parent[x]] == goal)
        {
            //如果x是左孩子,则右旋,反之左旋
            if(children[parent[x]][0] == x)
            {
                rotate_tree(x, 1);
            }
            else
            {
                rotate_tree(x, 0);
            }
        }
        else
        {
            int y = parent[x];
            int dir;

            if(children[parent[y]][0] == y)
            {
                dir = 1;
            }
            else
            {
                dir = 0;
            }
            //x节点与其父节点的方向不同,则两个方向各旋一次,先旋dir的相反方向
            if(children[y][dir] == x)
            {
                rotate_tree(x, !dir);
                rotate_tree(x, dir);
            }
            //x节点与其父节点的方向相同,则相同方向连续两次
            else
            {
                rotate_tree(y, dir);
                rotate_tree(x, dir);
            }
        }
    }
    //更新根结点
    if(goal == 0) root = x;
}

//插入新数据
int insert_tree(int val)
{
    int x = root;

    //从节点x开始找与val最接近的value[x]
    while(children[x][ val > value[x] ])
    {
        //不重复插入
        if(value[x] == val)
        {
            Splay(x, 0);
            return 0;
        }
        x = children[x][ val > value[x] ];
    }

    newNode(children[x][ val > value[x] ], x, val);
    //将新插入的结点更新至根结点
    Splay(children[x][ val > value[x] ], 0);
    return 1;
}

//中序遍历伸展树
void inOrderTraversal_tree(int tree)
{
    if(value[tree] != -1)
    {
        inOrderTraversal_tree(children[tree][0]);
        printf("%d ", value[tree]);
        inOrderTraversal_tree(children[tree][1]);
    }
}

//删除节点
int remove_tree(int x)
{
    //先将要删除的节点调整为根节点
    Splay(x, 0);
    int newTree;

    //如果x节点左孩子为空,则直接将x节点的右孩子接上去成为root节点
    if(children[x][0] == 0)
    {
        newTree = children[x][1];
        root = newTree;
        parent[newTree] = 0;
        //删除数据
        value[x] = -1;

    }
    else
    {
        //保存x节点的右孩子,便于最后插入。
        int rightTree = children[x][1];
        //从x节点的左孩子开始找最右边的孩子,即x节点左边键值最大的节点
        newTree = children[x][0];
        while(children[newTree][1])
        {
            newTree = children[newTree][1];
        }
        //将找到的newTree调整为根节点
        Splay(newTree, 0);
        //插入x节点的右孩子
        children[newTree][1] = rightTree;
        //如果右孩子不为空,设置其父节点为newTree
        if(rightTree)
            parent[rightTree] = newTree;
        //删除数据
        value[x] = -1;
    }
    return 1;
}

//查询数据 返回0表示无查询结果
int search_tree(int val)
{
    int tree = root;
    while(value[tree] != val && tree)
    {
        if(val > value[tree])
            tree = children[tree][1];
        else
            tree = children[tree][0];
    }

    //如果查询成功 调整至根节点
    if(tree) Splay(tree, 0);
    return tree;
}

//找x节点的前驱节点的值 返回其与x节点的键值的差
int search_pre(int x)
{
    int tree = children[x][0];
    if(!tree) return inf;
    while(children[tree][1])
    {
        tree = children[tree][1];
    }
    return value[x] - value[tree];
}

//找x节点的后驱节点的值 返回其与x节点的键值的差
int search_next(int x)
{
    int tree = children[x][1];
    if(!tree) return inf;
    while(children[tree][0])
    {
        tree = children[tree][0];
    }
    return value[tree] - value[x];
}

int main()
{
    int ans = 0;
    root = 0;
    cot = 0;
    memset(value, -1, sizeof(value));
    memset(children, 0, sizeof(children));
    memset(parent, -1, sizeof(parent));
    int n;
    scanf("%d", &n);
    for(int i = 1; i <= n; i++)
    {
        scanf("%d", &value[i]);
        if(i == 1)
        {
            ans = value[i];
            newNode(root, 0, value[i]);
            continue;
        }
        if(insert_tree(value[i]) == 0)continue;
        ans += min(search_pre(root), search_next(root));
    }

    printf("%d\n", ans);
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值