Monkey King(左偏树 可并堆)
我们知道如果要我们给一个序列排序,按照某种大小顺序关系,我们很容易想到优先队列,的确很方便,但是优先队列也有解决不了的问题,当题目要求你把两个优先队列合并的时候,这就实现不了了
优先队列只有插入 删除 取数的操作,但是却没有合并两个优先队列的操作。 这也是它的局限所在。
本次要介绍的左偏树拥有优先队列的所有功能,同时它还可以合并操作。 树的复杂度都比较低,一般log(n)就够了,左偏树也是如此,左偏树如果一个个结点暴力插入复杂度最大为nlog(n)
还有一种仿照二叉树的算法,这里不做介绍。 下面仔细说一下什么是左偏树:
H ← Merge(H1,H2):
Merge( ) 构造并返回一个包含H1和H2所有元素的新堆H。
左偏树的定义:左偏树(Leftist Tree)是一种可并堆的实现。左偏树是一棵二叉树,它的节点除了和二叉树的节点一样具有左右子树指针( left, right )外,
还有两个属性:键值和距离(dist)。键值上面已经说过,是用于比较节点的大小。距离则是如下定义的:
节点 i 称为外节点(external node),当且仅当节点 i 的左子树或右子树为空( left(i) = NULL 或 right(i) = NULL );
节点 i 的距离( dist( i ) )是节点 i 到它的后代中,最近的外节点所经过的边数。特别的,如果节点 i 本身是外节点,则它的距离为 0;
而空节点的距离规定为-1 (dist(NULL) = -1)。在本文中,有时也提到一棵左偏树的距离,这指的是该树根节点的距离。
左偏树满足下面两条基本性质:
[性质 1] 节点的键值小于或等于它的左右子节点的键值。
即 key(i)≤key(parent(i)) 这条性质又叫堆性质。符合该性质的树是堆有序
的(Heap-Ordered)。有了性质 1,我们可以知道左偏树的根节点是整棵树的最小 节点(也可以使得它最大),于是我们可以在 O(1) 的时间内完成取最小节点操作。
[性质 2] 节点的左子节点的距离不小于右子节点的距离。
即 dist(left(i))≥dist(right(i)) 这条性质称为左偏性质。性质 2 是为了使我们可以以更小的代价在优先队列的其它两个基本操作(插入节点、删除最小节点)
进行后维持堆性质。在后面我们就会看到它的作用。
这两条性质是对每一个节点而言的,因此可以简单地从中得出,左偏树的左右子树都是左偏树。
由这两条性质,我们可以得出左偏树的定义:左偏树是具有左偏性质的堆有序二叉树。
下图是一棵左偏树:

2.3 左偏树的性质
在前面一节中,本文已经介绍了左偏树的两个基本性质,下面本文将介绍左偏树的另外两个性质。
我们知道,一个节点必须经由它的子节点才能到达外节点。由于性质 2,一个节点的距离实际上就是这个节点一直沿它的右边到达一个外节点所经过的边数,也就是说,我们有
[性质 3] 节点的距离等于它的右子节点的距离加 1。
即 dist( i ) = dist( right( i ) ) + 1 外节点的距离为 0,由于性质 2,它的右子节点必为空节点。为了满足性质 3,故前面规定空节点的距离为-1。
我们的印象中,平衡树是具有非常小的深度的,这也意味着到达任何一个节点所经过的边数很少。左偏树并不是为了快速访问所有的节点而设计的,它的目的是快速访问最小节点以及在对树修改后快速的恢复堆性质。从图中我们可以看到它并不平衡,由于性质 2 的缘故,它的结构偏向左侧,不过距离的概念和树的深度并不同,左偏树并不意味着左子树的节点数或是深度一定大于右子树。
下面我们来讨论左偏树的距离和节点数的关系。
[引理 1] 若左偏树的距离为一定值,则节点数最少的左偏树是完全二叉树。
证明:由性质 2 可知,当且仅当对于一棵左偏树中的每个节点 i,都有dist(left(i)) = dist(right(i)) 时,该左偏树的节点数最少。显然具有这样性质的二叉树是完全二叉树。
[定理 1] 若一棵左偏树的距离为k,则这棵左偏树至少有 2k+1-1 个节点。
证明:由引理 1 可知,当这样的左偏树节点数最少的时候,是一棵完全二叉
树。距离为k的完全二叉树高度也为k,节点数为 2k+1-1,所以距离为k的左偏树
至少有 2k+1
-1 个节点。
作为定理 1 的推论,我们有:
[性质 4] 一棵 N 个节点的左偏树距离最多为 ⎣log(N+1)⎦ -1。
证明:设一棵N个节点的左偏树距离为k,由定理 1 可知,N ≥ 2k+1-1,因此k
≤ ⎣log(N+1)⎦ -1。
有了上面的 4 个性质,我们可以开始讨论左偏树的操作了
1、左偏树的合并
C ← Merge(A,B)
下图是一个合并过程的示例:

下面是合并的代码:
int Merge(int x,int y)//两棵树合并
{
if(!x) return y;//找到插入的地方了
if(!y) return x;
if(v[x]<v[y]) swap(x,y);//使得根节点最大
r[x]=Merge(r[x],y);//递归合并右子树和y
fa[r[x]]=x;//更新右子树的根
if(d[l[x]]<d[r[x]]) swap(l[x],r[x]);
d[x]=d[r[x]]+;
return x;//新的根
}
2、插入新节点:
int Insert(int x, int y)
{
return Merge(x, Init(y));
}
3、删除最小节点:
int Pop(int x)
{
int L=l[x];//取出左子树
int R=r[x];//取出右子树
fa[L]=L;//根变为自己
fa[R]=R;
v[x]/=;//取根节点 值除二
r[x]=l[x]=d[x]=;
return Merge(L,R);//左右子树合并 返回值为左右子树合并后新的根
}
下面看一道例题:
题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=1389
Monkey King
Time Limit: 10 Seconds Memory Limit: 32768 KB
Once in a forest, there lived N aggressive monkeys. At the beginning, they each does things in its own way and none of them knows each other. But monkeys can't avoid quarrelling, and it only happens between two monkeys who does not know each other. And when it happens, both the two monkeys will invite the strongest friend of them, and duel. Of course, after the duel, the two monkeys and all of their friends knows each other, and the quarrel above will no longer happens between these monkeys even if they have ever conflicted.
Assume that every money has a strongness value, which will be reduced to only half of the original after a duel(that is, 10 will be reduced to 5 and 5 will be reduced to 2).
And we also assume that every monkey knows himself. That is, when he is the strongest one in all of his friends, he himself will go to duel.
Input
There are several test cases, and each case consists of two parts.
First part: The first line contains an integer N(N<=100,000), which indicates the number of monkeys. And then N lines follows. There is one number on each line, indicating the strongness value of ith monkey(<=32768).
Second part: The first line contains an integer M(M<=100,000), which indicates there are M conflicts happened. And then M lines follows, each line of which contains two integers x and y, indicating that there is a conflict between the Xth monkey and Yth.
Output
For each of the conflict, output -1 if the two monkeys know each other, otherwise output the strongness value of the strongest monkey in all friends of them after the duel.
Sample Input
5
20
16
10
10
4
5
2 3
3 4
3 5
4 5
1 5
Sample Output
8
5
5
-1
10
题目大意:
题意: N(N<=10^5)只猴子,初始每只猴子为自己猴群的猴王,每只猴子有一个初始的力量值。这些猴子会有M次会面。每次两只猴子x,y会面,若x,y属于同一个猴群输出-1,否则将x,y所在猴群的猴王的力量值减半,然后合并这两个猴群。新猴群中力量值最高的为猴王。输出新猴王的力量值。
分析:涉及集合的查询,合并,取最值。 利用并查集和左偏树即可解决。
看代码:
#include<cstdio>
#include<iostream>
#include<math.h>
using namespace std;
const int maxn=2e5;
int tot,v[maxn],l[maxn],r[maxn],d[maxn],fa[maxn];
int Findset(int x)
{
if(fa[x]==x) return fa[x];
return fa[x]=Findset(fa[x]);
}
void Init(int x)//初始化
{
tot++;
v[tot]=x;//存对应的值
fa[tot]=tot;//并查集初始化
l[tot]=r[tot]=d[tot]=;//左子树和右子树和距离为0
}
int Merge(int x,int y)//两棵树合并
{
if(!x) return y;//找到插入的地方了
if(!y) return x;
if(v[x]<v[y]) swap(x,y);//使得根节点最大
r[x]=Merge(r[x],y);//递归合并右子树和y
fa[r[x]]=x;//更新右子树的根
if(d[l[x]]<d[r[x]]) swap(l[x],r[x]);
d[x]=d[r[x]]+;
return x;//新的根
}
int Pop(int x)
{
int L=l[x];//取出左子树
int R=r[x];//取出右子树
fa[L]=L;//根变为自己
fa[R]=R;
v[x]/=;//取根节点 值除二
r[x]=l[x]=d[x]=;
return Merge(L,R);//左右子树合并 返回值为左右子树合并后新的根
}
int Top(int x)
{
return v[x];
}
void solve(int x,int y)
{
int Left=Pop(x);//去掉x后 左右子树合并后新的根
int Right=Pop(y);//去掉y后 左右子树合并后新的根
Left=Merge(Left,x);//把更新后的值重新加入该树中
Right=Merge(Right,y);
Left=Merge(Left,Right);//两棵树合并
printf("%d\n",Top(Left));//两棵树合并之后的根
} int main()
{
int n,m,i,x,y;
while(scanf("%d",&n)!=EOF)
{
tot=;
for(i=;i<=n;i++)
{
scanf("%d",&x);
Init(x);
}
scanf("%d",&m);
for(i=;i<=m;i++)
{
scanf("%d%d",&x,&y);
int fx=Findset(x);
int fy=Findset(y);
if(fx==fy) printf("-1\n");
else solve(fx,fy);
}
}
return ;
}
Monkey King(左偏树 可并堆)的更多相关文章
- hdu 1512 Monkey King 左偏树
题目链接:HDU - 1512 Once in a forest, there lived N aggressive monkeys. At the beginning, they each does ...
- ZOJ2334 Monkey King 左偏树
ZOJ2334 用左偏树实现优先队列最大的好处就是两个队列合并可以在Logn时间内完成 用来维护优先队列森林非常好用. 左偏树代码的核心也是两棵树的合并! 代码有些细节需要注意. #include&l ...
- zoj 2334 Monkey King/左偏树+并查集
原题链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=1389 大致题意:N只相互不认识的猴子(每只猴子有一个战斗力值) 两只 ...
- HDU1512 ZOJ2334 Monkey King 左偏树
欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - ZOJ2334 题目传送门 - HDU1512 题意概括 在一个森林里住着N(N<=10000)只猴子. ...
- HDU 1512 Monkey King (左偏树+并查集)
题意:在一个森林里住着N(N<=10000)只猴子.在一开始,他们是互不认识的.但是随着时间的推移,猴子们少不了争斗,但那只会发生在互不认识 (认识具有传递性)的两只猴子之间.争斗时,两只猴子都 ...
- hdu 1512 Monkey King —— 左偏树
题目:http://acm.hdu.edu.cn/showproblem.php?pid=1512 很简单的左偏树: 但突然对 rt 的关系感到混乱,改了半天才弄对: 注意是多组数据! #includ ...
- hdu1512 Monkey King(左偏树 + 并查集)
Once in a forest, there lived N aggressive monkeys. At the beginning, they each does things in its o ...
- HDU 1512 Monkey King ——左偏树
[题目分析] 也是堆+并查集. 比起BZOJ 1455 来说,只是合并的方式麻烦了一点. WA了一天才看到是多组数据. 盲人OI (- ̄▽ ̄)- Best OI. 代码自带大常数,比启发式合并都慢 [ ...
- LuoguP1456 Monkey King (左偏树)
struct LeftTree{ int l,r,val,dis; }t[N]; int fa[N]; inline int Find(int x){ return x == fa[x] ? x : ...
- [note]左偏树(可并堆)
左偏树(可并堆)https://www.luogu.org/problemnew/show/P3377 题目描述 一开始有N个小根堆,每个堆包含且仅包含一个数.接下来需要支持两种操作: 操作1: 1 ...
随机推荐
- Cadence如何自定义快捷键
第一步:将pcbenv复制到SPB_Data下,并覆盖原来的文件 第二步:打开pcbenv文件,将script和views文件夹以及env复制到cadence安装目录\SPB_16.3\share\p ...
- 第一个SpringMVC程序(最简单的)
注册中央调度器,这个中央调度器就是org.springframework.web.servlet.DispatcherServlet这个类(web.xml servlet-name节点的名字,必须 ...
- sql去掉换行符
select replace(replace(ServiceCall,CHAR(13),''),CHAR(10),'') from outbound_complaint where bz1='朱成 ...
- Centos7安装配置JDK8
Centos7安装配置JDK8 一.准备工作 第一步,去甲骨文官网下载Jdk相应的版本,我这里下载的是jdk1.8. 第二步将你从官网上下载下来的jdk使用FTP工具上传到云服务器上的相应目录,我的是 ...
- ios中的奇怪崩溃Signal和EXC_BAD_ACCESS错误分析
什么是Signal 在计算机科学中,信号(英语:Signals)是Unix.类Unix以及其他POSIX兼容的操作系统中进程间通讯的一种有限制的方式.它是一种异步的通知机制,用来提醒进程一个事件已经发 ...
- Linux安装步骤
1.查看Linux系统是32位还是64位 #查看系统位数 getconf LONG_BIT #或者 uname -m #或者 arch #或者 file /sbin/init 2.IP配置 网络选择桥 ...
- 树状数组【bzoj3155】: Preprefix sum
3155: Preprefix sum 题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=3155 把给出的a_i当成查分数组d_i做就可以了 ...
- 5分钟构建无服务器敏感词过滤后端系统(基于FunctionGraph)
摘要:开发者通过函数工作流,无需配置和管理服务器,以无服务器的方式构建应用,便能开发出一个弹性高可用的后端系统.托管函数具备以毫秒级弹性伸缩.免运维.高可靠的方式运行,极大地提高了开发和运维效率,减小 ...
- Windows多个应用程序共享全局变量,静态变量
默认情况下exe不同实例使用copy-on-write技术避免共享数据,比如运行了两个exe,最开始它们使用的都是一份虚拟内存页,然后第一个实例修改了全局变量, 这时候COW就会复制那一页,然后将第一 ...
- 数据结构20:KMP算法(快速模式匹配算法)详解
通过上一节的介绍,学习了串的普通模式匹配算法,大体思路是:模式串从主串的第一个字符开始匹配,每匹配失败,主串中记录匹配进度的指针 i 都要进行 i-j+1 的回退操作(这个过程称为“指针回溯”),同时 ...