wikioi 1396 伸展树(模板)

题目描述 Description

Tiger最近被公司升任为营业部经理,他上任后接受公司交给的第一项任务便是统计并分析公司成立以来的营业情况。

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

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

当最小波动值越大时,就说明营业情况越不稳定。

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

第一天的最小波动值为第一天的营业额。

输入描述 Input Description

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

输出描述 Output Description

输出文件仅有一个正整数,即每天最小波动值之和,小于231

样例输入 Sample Input

6

5

1

2

5

4

6

样例输出 Sample Output

12

数据范围及提示 Data Size & Hint

结果说明:5+|1-5|+|2-1|+|5-5|+|4-5|+|6-5|=5+4+1+0+1+1=12

思路:伸展树从下午开始看,以前看过了,觉得太麻烦了,然后数据结构老师讲过之后,我学明白了,考试都秒过,然后现在了才开始做题。因为在数据结构实现中,本人比较喜欢数组替代指针,所以尼玛找了好久的模板才找到这个让自己觉得很爽又很能理解的。其他都是指针来实现,代码又长,烦死了。

因为找了一下午加一晚上,实在找不到数组实现的伸展树了,就查了这个入门题的解题报告,终于让我发现了这个好代码,嘿嘿。

参考代码博客:http://blog.csdn.net/rowanhaoa/article/details/24436703

说明:伸展树是建立在排序二叉树上的,所以左子树所有的值都比根小,根又都比右子树所有的值小。因为伸展树是平衡二叉树,所以具备平衡二叉树的特点,所有功能的平摊时间复杂度都是O(log n),所以在数据结构中用得非常爽,代码又是可以接受的复杂度和长度。而且每个结点也可以是区间,可以代替线段树,你说爽不爽。伸展树最大的优点是分裂和合并都是log n,所以比别的当然很快,在处理很多问题上自然有很多优点。从此,爱上伸展树……

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<cmath>
#include<bitset>
#define mem(a,b) memset(a,b,sizeof(a))
#define INF 1000000007
#define maxn 100010
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
struct splaytree //伸展树
{
    int pre[maxn];//前驱
    int key[maxn];//键值
    int ch[maxn][2];//左右孩子,0为左孩子,1为右孩子
    int root,tot;//根节点,节点数量
    splaytree()
    {
        tot=root=0;
        mem(ch,0);
        mem(pre,0);
        mem(key,0);
    }
    void newnode(int &r,int father,int k)
    {//在r位置,父亲为father,新建一个值点。
        r=++tot;
        pre[r]=father;
        key[r]=k;
        ch[r][0]=ch[r][1]=0;
    }
    void rotate(int x,int kind)//kind=1表示右旋,kind=0表示左旋
    {
        int y=pre[x];
        ch[y][!kind]=ch[x][kind];
        pre[ch[x][kind]]=y;
        ch[x][kind]=y;
        if(pre[y]) ch[pre[y]][ch[pre[y]][1]==y]=x;
        pre[x]=pre[y];
        pre[y]=x;
    }
    void splay(int x,int goal) //把x伸展到目标位置
    {
        while(pre[x]!=goal)
        {
            if(pre[pre[x]]==goal) rotate(x,ch[pre[x]][0]==x);
            else
            {
                int y=pre[x];
                int kind=ch[pre[y]][0]==y;
                if(ch[y][kind]==x) rotate(x,!kind),rotate(x,kind);
                else rotate(y,kind),rotate(x,kind);
            }
        }
        if(!goal) root=x;//把root更新成x
    }
    int insert(int x)//插入值x
    {
        int r=root;
        while(ch[r][key[r]<x])
        {
            if(key[r]==x) {splay(r,0);return 0;}
            r=ch[r][key[r]<x];
        }
        newnode(ch[r][x>key[r]],r,x);
        splay(ch[r][x>key[r]],0);
        return 1;
    }
    int find(int x)//查找键值为x的结点编号
    {
        int r=root;
        if(!r) return -INF;//树未建,未找到
        if(key[r]==x) return r;
        while(ch[r][x>key[r]])
        {
            r=ch[r][x>key[r]];
            if(key[r]==x) return r;
        }
        return -INF;//未找到
    }
    int get_pre(int x)//寻找节点x的前驱的值,未找到则返回INF
    {
        int r=ch[x][0];
        if(!r) return -INF;
        while(ch[r][1]) r=ch[r][1];
        return key[r];
    }
    int get_next(int x)
    {
        int r=ch[x][1];
        if(!r) return INF;
        while(ch[r][0]) r=ch[r][0];
        return key[r];
    }
};
int main()
{
    int sum=0,a,n;
    splaytree tree;
    cin>>n;
    for(int i=0;i<n;i++)
    {
        scanf("%d",&a);
        if(!i)
        {
            sum+=a;
            tree.newnode(tree.root,0,a);
            continue;
        }
        if(!tree.insert(a)) continue;
        //因为既然前面已经有a,那这天的相减为0,所以下面就不用继续了
        int x=tree.get_pre(tree.root);//因为insert之后根root即为当前a的
        int y=tree.get_next(tree.root);//故可从根查找
        sum+=min(a-x,y-a);
    }
    cout<<sum<<endl;
    return 0;
}

wikioi 1396 伸展树(模板)

时间: 2024-10-02 19:47:56

wikioi 1396 伸展树(模板)的相关文章

wikioi 1285 伸展树delete操作

题目描述 Description 最近,阿Q开了一间宠物收养所.收养所提供两种服务:收养被主人遗弃的宠物和让新的主人领养这些宠物. 每个领养者都希望领养到自己满意的宠物,阿Q根据领养者的要求通过他自己发明的一个特殊的公式,得出该领养者希望领养的宠物的特点值a(a是一个正整数,a<2^31),而他也给每个处在收养所的宠物一个特点值.这样他就能够很方便的处理整个领养宠物的过程了,宠物收养所总是会有两种情况发生:被遗弃的宠物过多或者是想要收养宠物的人太多,而宠物太少. 1. 被遗弃的宠物过多时,假若到

splay伸展树模板

1 struct SplayTree 2 { 3 4 const static int maxn = 1e5 + 15; 5 6 int tot,root,ch[maxn][2], key[maxn], sz[maxn], lz[maxn], fa[maxn]; 7 8 void init( int x, int v = 0, int par = 0 ) 9 { 10 ch[x][0]=ch[x][1]=0, fa[x]= par, key[x] = v, sz[x] = 1; 11 } 12

HYSBZ 1503 郁闷的出纳员 伸展树

题目链接: https://vjudge.net/problem/26193/origin 题目描述: 中文题面....... 解题思路: 伸展树, 需要伸展树的模板, 突然发现自己昨天看到的模板不是太好, 现在又新找了一个,  很简练, 自己将模板的实现从头到尾看了一遍, 觉得数组实现的实在是非常的巧妙, 然后自己照着敲了一遍, 边敲边看, 崩掉了.....肯定是哪里手残了, 没有必要浪费时间去改了, 有那时间不如看点别的 代码: #include <iostream> #include &

HNOI2002(伸展树)

营业额统计 Time Limit:5000MS     Memory Limit:165888KB     64bit IO Format:%lld & %llu Submit Status Description 营业额统计 Tiger最近被公司升任为营业部经理,他上任后接受公司交给的第一项任务便是统计并分析公司成立以来的营业情况. Tiger拿出了公司的账本,账本上记录了公司成立以来每天的营业额.分析营业情况是一项相当复杂的工作.由于节假日,大减价或者是其他情况的时候,营业额会出现一定的波动

【学时总结】◆学时&#183;VI◆ SPLAY伸展树

◆学时·VI◆ SPLAY伸展树 平衡树之多,学之不尽也-- ◇算法概述 二叉排序树的一种,自动平衡,由 Tarjan 提出并实现.得名于特有的 Splay 操作. Splay操作:将节点u通过单旋.双旋移动到某一个指定位置. 主要目的是将访问频率高的节点在不改变原顺序的前提下移动到尽量靠近根节点的位置,以此来解决同一个(相似)问题的多次查询. 但是在非降序查询每一个节点后,Splay 树会变为一条链,降低运算效率. ◇原理&细解 (1)旋转操作 二叉排序树必须满足 左儿子<根节点<右

# 伸展树 Splay

伸展树 Splay 维基百科上称为伸展树,但是国内好像一般叫平衡树,是众多平衡树中比较优秀的一种. 平衡树左旋右旋不会影响中序遍历顺序. 一棵平衡树的中序遍历顺序是值递增排序的,相当于从小到大到大排了一次序. 平衡树的作用: 平衡树其实就是一棵二叉搜索树,set和map都是平衡树实现. 一棵二叉搜索树理论深度是\(O(log(n))\),但是当二叉树退化成链表的时候,深度就变成了\(O(n)\),很多\(O(log)\)级别从操作会退化成\(O(n)\)线性级别的操作.平衡树就是在不改变二叉搜索

算法学习:伸展树(splay)

[定义] [平衡树] 每个叶子结点的深度差不超过1的二叉树 [伸展树] [常用问题] splay的操作,通过左旋右旋,将某个结点通过旋转旋转至根节点,使树的结构发生变化,尽可能的平衡 并且因为左旋右旋的性质,当原树是一个二叉排序树的时候,splay依旧能够使原树保持二叉排序树的性质 左旋右旋图片 [模板题] [luogu P3369]普通平衡树 [题意]实现一颗二叉排序树的增删查改 [注]对数据结构的理解见注释 [代码] #include<cstdio> #include<iostrea

poj3630 Phone List (trie树模板题)

Phone List Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 26328   Accepted: 7938 Description Given a list of phone numbers, determine if it is consistent in the sense that no number is the prefix of another. Let's say the phone catalogu

HDU 1251 Trie树模板题

1.HDU 1251 统计难题  Trie树模板题,或者map 2.总结:用C++过了,G++就爆内存.. 题意:查找给定前缀的单词数量. #include<iostream> #include<cstring> #include<cmath> #include<queue> #include<algorithm> #include<cstdio> #define max(a,b) a>b?a:b #define F(i,a,b