wikioi 1396 伸展树(两个模板)

2024-06-08 23:38
文章标签 模板 两个 伸展 wikioi 1396

本文主要是介绍wikioi 1396 伸展树(两个模板),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

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

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

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

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

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

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

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

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

6

5

1

2

5

4

6

12

结果说明: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 伸展树(两个模板)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1043643

相关文章

poj3468(线段树成段更新模板题)

题意:包括两个操作:1、将[a.b]上的数字加上v;2、查询区间[a,b]上的和 下面的介绍是下解题思路: 首先介绍  lazy-tag思想:用一个变量记录每一个线段树节点的变化值,当这部分线段的一致性被破坏我们就将这个变化值传递给子区间,大大增加了线段树的效率。 比如现在需要对[a,b]区间值进行加c操作,那么就从根节点[1,n]开始调用update函数进行操作,如果刚好执行到一个子节点,

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

poj 1258 Agri-Net(最小生成树模板代码)

感觉用这题来当模板更适合。 题意就是给你邻接矩阵求最小生成树啦。~ prim代码:效率很高。172k...0ms。 #include<stdio.h>#include<algorithm>using namespace std;const int MaxN = 101;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int n

uva 1342 欧拉定理(计算几何模板)

题意: 给几个点,把这几个点用直线连起来,求这些直线把平面分成了几个。 解析: 欧拉定理: 顶点数 + 面数 - 边数= 2。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstring>#include <cmath>#inc

uva 11178 计算集合模板题

题意: 求三角形行三个角三等分点射线交出的内三角形坐标。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstring>#include <cmath>#include <stack>#include <vector>#include <

poj 2104 and hdu 2665 划分树模板入门题

题意: 给一个数组n(1e5)个数,给一个范围(fr, to, k),求这个范围中第k大的数。 解析: 划分树入门。 bing神的模板。 坑爹的地方是把-l 看成了-1........ 一直re。 代码: poj 2104: #include <iostream>#include <cstdio>#include <cstdlib>#include <al

最大流、 最小费用最大流终极版模板

最大流  const int inf = 1000000000 ;const int maxn = 20000 , maxm = 500000 ;struct Edge{int v , f ,next ;Edge(){}Edge(int _v , int _f , int _next):v(_v) ,f(_f),next(_next){}};int sourse , mee

两个月冲刺软考——访问位与修改位的题型(淘汰哪一页);内聚的类型;关于码制的知识点;地址映射的相关内容

1.访问位与修改位的题型(淘汰哪一页) 访问位:为1时表示在内存期间被访问过,为0时表示未被访问;修改位:为1时表示该页面自从被装入内存后被修改过,为0时表示未修改过。 置换页面时,最先置换访问位和修改位为00的,其次是01(没被访问但被修改过)的,之后是10(被访问了但没被修改过),最后是11。 2.内聚的类型 功能内聚:完成一个单一功能,各个部分协同工作,缺一不可。 顺序内聚:

C++语法知识点合集:11.模板

文章目录 一、非类型模板参数1.非类型模板参数的基本形式2.指针作为非类型模板参数3.引用作为非类型模板参数4.非类型模板参数的限制和陷阱:5.几个问题 二、模板的特化1.概念2.函数模板特化3.类模板特化(1)全特化(2)偏特化(3)类模板特化应用示例 三、模板分离编译1.概念2.模板的分离编译 模版总结 一、非类型模板参数 模板参数分类类型形参与非类型形参 非类型模板

Smarty模板引擎工作机制(一)

深入浅出Smarty模板引擎工作机制,我们将对比使用smarty模板引擎和没使用smarty模板引擎的两种开发方式的区别,并动手开发一个自己的模板引擎,以便加深对smarty模板引擎工作机制的理解。 在没有使用Smarty模板引擎的情况下,我们都是将PHP程序和网页模板合在一起编辑的,好比下面的源代码: <?php$title="深处浅出之Smarty模板引擎工作机制";$content=