题目描写叙述 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,所以比别的当然非常快,在处理非常多问题上自然有非常多长处。从此,爱上伸展树……

模板一:

这个伸展树的结点是用数据写的。是參照某个博客的,比較方便,只是有时候有点慢,由于是数组。所以debug不出来的几率比較高。

#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;
}

模板二:结构体结点

这个是參照百度百科上的代码写的,刚開始写的时候执行这题的数据出错,然后debug了好久,还以为代码中的splay和旋转错了。都已经放弃然后去找别的模板了。

可是后面还是输出当中过程的数据才知道错哪了。由于和数组的模板不一样,所以是自己传參的时候传错了,本来是传结点编号的,我传成值了。

为什么上面的模板不用。又找了这个呢。是由于我用上面的模板写一道题的时候严重超时,然后改又改不了了,然后自己又找了这个模板。比較好理解。数组类的模板写法太飘逸了,咱等寻常人一时难以接受,自己写的删除结点函数又不太会。所以仅仅能临时放弃数组的模板了。

用以下这个比較好理解。又好写,尽管代码是长了点。

这个模板不足之处就是insert的时候没有推断是否已经存在本值,size就直接加了。

这个在有些题面前改的东西就多了,上面数组模板就有优势了。

#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 splayNode
{
int l, r, fa, val, size;
};
struct splaytree
{
splayNode t[maxn];
int root, tot;
void create()
{
root = 1, tot = 2;
t[1].val = -INF;
t[2].val = INF;
t[1].r = t[1].size = 2;
t[2].fa = t[2].size = 1;
}
void dfs(int x)
{
if(x)
{
dfs(t[x].l);
printf("结点%2d: 左儿子 %2d 右儿子 %2d 父结点 %2d 键值为:%2d\n",x,t[x].l,t[x].r,t[x].fa,t[x].val);
dfs(t[x].r);
}
}
void debug()
{
printf("根为:%d\n",root);
dfs(root);
}
void update(int x)
{
t[x].size=t[t[x].l].size+t[t[x].r].size+1;
}
void left(int x)
{
int fa = t[x].fa;
t[x].fa = t[fa].fa;
if (t[t[fa].fa].l==fa) t[t[fa].fa].l=x;
if (t[t[fa].fa].r==fa) t[t[fa].fa].r=x;
t[fa].fa = x;
t[fa].r = t[x].l;
t[t[x].l].fa = fa;
t[x].l = fa;
update(fa);
}
void right(int x)
{
int fa = t[x].fa;
t[x].fa = t[fa].fa;
if (t[t[fa].fa].l==fa) t[t[fa].fa].l=x;
if (t[t[fa].fa].r==fa) t[t[fa].fa].r=x;
t[fa].fa = x;
t[fa].l = t[x].r;
t[t[x].r].fa = fa;
t[x].r = fa;
update(fa);
}
void splay(int x, int FA = 0)
{
while (t[x].fa != FA)
{
int fa = t[x].fa;
if (t[fa].fa == FA)
{
if (t[fa].l==x) right(x);
else left(x);
}
else if (t[t[fa].fa].l==fa)
{
if(t[fa].l==x) right(fa),right(x);
else left(x), right(x);
}
else if (t[fa].l==x) right(x),left(x);
else left(fa), left(x);
}
update(x);
if (!FA) root = x;
}
int lower_bound(int val)
{
int ans = 0, la = 0;
for (int x = root; x;)
{
la = x;
if (t[x].val>=val) ans=x,x=t[x].l;
else x = t[x].r;
}
splay(la);
return ans;
}
void insert(int val)
{
for (int x = root;;)
{
++t[x].size;
if (t[x].val >= val)
{
if (t[x].l) x = t[x].l;
else
{
t[x].l = ++tot;
t[tot].size = 1;
t[tot].fa = x;
t[tot].val = val;
splay(tot);
return;
}
}
else if (t[x].r) x = t[x].r;
else
{
t[x].r = ++tot;
t[tot].size = 1;
t[tot].fa = x;
t[tot].val = val;
splay(tot);
return;
}
}
}
int get_lower(int a)
{
splay(a);
for (a = t[a].l; t[a].r; a = t[a].r);
return t[a].val;
}
int get_upper(int a)
{
splay(a);
for (a = t[a].r; t[a].l; a = t[a].l);
return t[a].val;
}
int get_rank(int a)
{
splay(a);
return t[t[a].l].size;
}
void del(int l, int r)
{
l = get_lower(l);
r = get_upper(r);
splay(l);
splay(r, l);
t[r].l = 0;
update(r);
update(l);
}
int get_kth(int k)
{
++k;
for (int x=root;;)
{
if (t[t[x].l].size==k-1)
return x;
if (t[t[x].l].size>=k) x=t[x].l;
else k-=t[t[x].l].size+1,x=t[x].r;
}
}
}tree;
map<int,int>Map;
int main()
{
int n,a,sum=0;
tree.create();
cin>>n;
for(int i=0;i<n;i++)
{
scanf("%d",&a);
//tree.debug();
if(!i) sum+=a,tree.insert(a),Map[a]=1;
else
{
if(Map[a]) continue;
Map[a]=1;
tree.insert(a);
//tree.debug();
int x=tree.get_lower(tree.root);
int y=tree.get_upper(tree.root);
//cout<<"+++"<<x<<' '<<y<<endl;
sum+=min(a-x,y-a);
}
}
cout<<sum<<endl;
return 0;
}

wikioi 1396 伸展树(两个模板)的更多相关文章

  1. Splay伸展树入门(单点操作,区间维护)附例题模板

    Pps:终于学会了伸展树的区间操作,做一个完整的总结,总结一下自己的伸展树的单点操作和区间维护,顺便给未来的自己总结复习用. splay是一种平衡树,[平均]操作复杂度O(nlogn).首先平衡树先是 ...

  2. HYSBZ 1500 维修数列(伸展树模板)

    题意: 题解:典型伸展树的题,比较全面. 我理解的伸展树: 1 伸展操作:就是旋转,因为我们只需保证二叉树中序遍历的结果不变,所以我们可以旋转来保持树的平衡,且旋转有左旋与右旋.通过这种方式保证不会让 ...

  3. 【模板】Splay(伸展树)普通平衡树(数据加强版)/洛谷P6136

    题目链接 https://www.luogu.com.cn/problem/P6136 题目大意 需要写一种数据结构,来维护一些非负整数( \(int\) 范围内)的升序序列,其中需要提供以下操作: ...

  4. Splay 伸展树

    废话不说,有篇论文可供参考:杨思雨:<伸展树的基本操作与应用> Splay的好处可以快速分裂和合并. ===============================14.07.26更新== ...

  5. UVa 11922 - Permutation Transformer 伸展树

    第一棵伸展树,各种调试模板……TVT 对于 1 n 这种查询我处理的不太好,之前序列前后没有添加冗余节点,一直Runtime Error. 后来加上冗余节点之后又出了别的状况,因为多了 0 和 n+1 ...

  6. [SinGuLaRiTy] SplayTree 伸展树

    [SinGuLaRiTy-1010]Copyrights (c) SinGuLaRiTy 2017. All Rights Reserved. Some Method Are Reprinted Fr ...

  7. 伸展树Splay【非指针版】

    ·伸展树有以下基本操作(基于一道强大模板题:codevs维护队列): a[]读入的数组;id[]表示当前数组中的元素在树中节点的临时标号;fa[]当前节点的父节点的编号;c[][]类似于Trie,就是 ...

  8. 二叉查找树,AVL树,伸展树【CH4601普通平衡树】

    最近数据结构刚好看到了伸展树,在想这个东西有什么应用,于是顺便学习一下. 二叉查找树(BST),对于树上的任意一个节点,节点的左子树上的关键字都小于这个节点的关键字,节点的右子树上的关键字都大于这个节 ...

  9. BZOJ 1269 文本编辑器editor(伸展树)

    题意 https://www.lydsy.com/JudgeOnline/problem.php?id=1269 思路 伸展树(\(\text{splay}\))功能比较齐全的模板,能较好的体现 \( ...

随机推荐

  1. [HTML] 如何使用robots.txt防止搜索引擎抓取页面

    Robots.txt 文件对抓取网络的搜索引擎漫游器(称为漫游器)进行限制.这些漫游器是自动的,在它们访问网页前会查看是否存在限制其访问特定网页的 robots.txt 文件.如果你想保护网站上的某些 ...

  2. php word转pdf

    我们知道,PHP语言的合理运用可以帮助我们实现导出Word文档的功能.今天我们将为大家介绍PHP处理Word转PDF的相关实现方法. PHP关键字this指向当前对象指针 PHP self关键字指向类 ...

  3. OpenCascade 边界表示法(BRep)

    转自 http://www.cppblog.com/eryar/archive/2013/08/20/202678.html Topology and Geometry in OpenCascade- ...

  4. JS form 表单收集 数据 formSerialize

    做后台系统的时候通常会用到form表单来做数据采集:每次一个字段一个字段的去收集就会很麻烦,网站也有form.js插件可以进行表单收集,并封装成一个对象,通过ajax方法传到后台:现在介绍一种直觉采集 ...

  5. 多个tomcat配置,解决冲突问题

    一.一般修改 路径: /opt/apache-tomcat/conf/server.xml 1.第一个tomcat使用默认配置 2.第二个tomcat一般配置 二.特殊修改 1.第二个tomcat特殊 ...

  6. CSS3实现简单的幻灯片

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  7. 创建我们第一个Monad

    上一篇中介绍了如何使用amplified type, 如IEnumerable<T>,如果我们能找到组合amplified type函数的方法,就会更容易写出强大的程序. 我们已经说了很多 ...

  8. express jade ejs 为什么要用这些?

    express是快速构建web应用的一个框架   线上文档 http://www.expressjs.com.cn/ 不用express行不行呢?    看了网上的回答:不用express直接搭,等你 ...

  9. (转载)Activity-数据状态的保存

    由于手机是便捷式移动设备,掌握在用户的手中,它的展示方向我们是无法预知的,具有不确定性.平时我们拿着手机多数为竖屏,但有时候我们感觉累了也会躺着去使用手机,那么这时手机屏幕的展示方向可能已经被用户切换 ...

  10. MySQL 5.6 Reference Manual-14.4 InnoDB Configuration

    14.4 InnoDB Configuration 14.4.1 InnoDB Initialization and Startup Configuration 14.4.2 Configuring ...