[note]左偏树(可并堆)
左偏树(可并堆)https://www.luogu.org/problemnew/show/P3377
题目描述
一开始有N个小根堆,每个堆包含且仅包含一个数。接下来需要支持两种操作:
操作1: 1 x y 将第x个数和第y个数所在的小根堆合并(若第x或第y个数已经被删除或第x和第y个数在用一个堆内,则无视此操作)
操作2: 2 x 输出第x个数所在的堆最小数,并将其删除(若第x个数已经被删除,则输出-1并无视删除操作)
输入格式:
第一行包含两个正整数N、M,分别表示一开始小根堆的个数和接下来操作的个数。
第二行包含N个正整数,其中第i个正整数表示第i个小根堆初始时包含且仅包含的数。
接下来M行每行2个或3个正整数,表示一条操作,格式如下:
操作1 : 1 x y
操作2 : 2 x
输出格式:
输出包含若干行整数,分别依次对应每一个操作2所得的结果。
输入样例:
5 5
1 5 4 2 3
1 1 5
1 2 5
2 2
1 4 2
2 2
输出样例:
1
2
说明
当堆里有多个最小值时,优先删除原序列的靠前的,否则会影响后续操作1导致WA。
时空限制:1000ms,128M
数据规模:
对于30%的数据:N<=10,M<=10
对于70%的数据:N<=1000,M<=1000
对于100%的数据:N<=100000,M<=100000
样例说明:
初始状态下,五个小根堆分别为:{1}、{5}、{4}、{2}、{3}。
第一次操作,将第1个数所在的小根堆与第5个数所在的小根堆合并,故变为四个小根堆:{1,3}、{5}、{4}、{2}。
第二次操作,将第2个数所在的小根堆与第5个数所在的小根堆合并,故变为三个小根堆:{1,3,5}、{4}、{2}。
第三次操作,将第2个数所在的小根堆的最小值输出并删除,故输出1,第一个数被删除,三个小根堆为:{3,5}、{4}、{2}。
第四次操作,将第4个数所在的小根堆与第2个数所在的小根堆合并,故变为两个小根堆:{2,3,5}、{4}。
第五次操作,将第2个数所在的小根堆的最小值输出并删除,故输出2,第四个数被删除,两个小根堆为:{3,5}、{4}。
故输出依次为1、2。
给大家介绍一篇左偏树讲解
以下摘自国集2005论文集
左偏树(Leftist Tree)
是一种可并堆的实现。左偏树是一棵二叉树,它的节点除了和二叉树的节点一样具有左右子树指针外,还有两个属性:点权(键值)和距离(dist).
左偏树满足下面两条基本性质:
[性质1] 节点的键值小于或等于它的左右子节点的键值.
这条性质又叫堆性质。符合该性质的树是堆有序的。
有了性质1,我们可以知道左偏树的根节点是整棵树的最小节点,于是我们可以在O(1) 的时间内完成取最小节点操作。
[性质2] 节点的左子节点的距离不小于右子节点的距离.
即dist(left(i))≥dist(right(i)) 这条性质称为左偏性质。
性质2是为了使我们可以以更小的代价在优先队列的其它两个基本操作(插入节点、删除最小节点)进行后维持堆性质。
这两条性质是对每一个节点而言的,因此可以简单地从中得出,左偏树的左右子树都是左偏树。
由这两条性质,我们可以得出左偏树的定义:左偏树是具有左偏性质的堆有序二叉树。
习惯上常用根节点序号表示左偏树
#define RG register
#include<cstdio>
#include<iostream>
using namespace std;
const int N=1e5+5;
inline int read()
{
RG int x=0,w=1;RG char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x*w;
}
int n,m;
int num[N],ls[N],rs[N],root[N],dist[N];//num[]为每个点的值,ls[]和rs[]为左右儿子,root[]表示该节点所在堆的堆顶编号(左偏树根节点),dist[i]表示节点i节点i到它的后代中最近的外节点所经过的边数[某节点称为外节点,当且仅当该节点的左子树或右子树为空].
bool del[N];//判断点是否被删除
int find(int x){return x==root[x]?x:root[x]=find(root[x]);}//并查集找根节点
int Merge(int a,int b)
{
if(!a||!b)return a+b;//若某一子树为空,返回另一棵子树
if(num[a]>num[b]||(num[a]==num[b]&&a>b))swap(a,b);//维护小根堆[权值相同,序号优先]
rs[a]=Merge(rs[a],b);//每次将a的右子树与b合并
if(dist[ls[a]]<dist[rs[b]])swap(ls[a],rs[a]);//交换左右儿子[保证左偏]
dist[a]=dist[rs[a]]+1;
return a;
}
int main()
{
n=read();
m=read();
for(int i=1;i<=n;i++)num[i]=read(),root[i]=i;
while(m--)
{
int f=read();
if(f==1)
{
int a=read(),b=read();
int x=find(a),y=find(b);
if(x==y||del[a]||del[b])continue;
root[x]=root[y]=Merge(x,y);//修改根节点
}
else
{
int x=read();
if(del[x]){printf("-1\n");continue;}
x=find(x);
printf("%d\n",num[x]);
del[x]=true;
root[x]=Merge(ls[x],rs[x]);//修改根节点
if(ls[x]==root[x])root[ls[x]]=ls[x];//将根节点的根置为自己[防止find()递归出错]
if(rs[x]==root[x])root[rs[x]]=rs[x];//将根节点的根置为自己
ls[x]=rs[x]=0;//清空被删除点的左右儿子
}
}
return 0;
}
[note]左偏树(可并堆)的更多相关文章
- bzoj2809 [Apio2012]dispatching——左偏树(可并堆)
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2809 思路有点暴力和贪心,就是 dfs 枚举每个点作为管理者: 当然它的子树中派遣出去的忍者 ...
- HDU3031 To Be Or Not To Be 左偏树 可并堆
欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - HDU3031 题意概括 喜羊羊和灰太狼要比赛. 有R次比赛. 对于每次比赛,首先输入n,m,n表示喜羊羊和灰 ...
- HDU5818 Joint Stacks 左偏树,可并堆
欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - HDU5818 题意概括 有两个栈,有3种操作. 第一种是往其中一个栈加入一个数: 第二种是取出其中一个栈的顶 ...
- BZOJ 4003: [JLOI2015]城池攻占 左偏树 可并堆
https://www.lydsy.com/JudgeOnline/problem.php?id=4003 感觉就是……普通的堆啊(暴论),因为这个堆是通过递归往右堆里加一个新堆或者新节点的,所以要始 ...
- Monkey King(左偏树 可并堆)
我们知道如果要我们给一个序列排序,按照某种大小顺序关系,我们很容易想到优先队列,的确很方便,但是优先队列也有解决不了的问题,当题目要求你把两个优先队列合并的时候,这就实现不了了 优先队列只有插入 删除 ...
- BZOJ 5494: [2019省队联测]春节十二响 (左偏树 可并堆)
题意 略 分析 稍微yy一下可以感觉就是一个不同子树合并堆,然后考场上写了一发左偏树,以为100分美滋滋.然而发现自己傻逼了,两个堆一一对应合并后剩下的一坨直接一次合并进去就行了.然鹅我这个sb把所有 ...
- [luogu3377][左偏树(可并堆)]
题目链接 思路 左偏树的模板题,参考左偏树学习笔记 对于这道题我是用一个并查集维护出了哪些点是在同一棵树上,也可以直接log的往上跳寻找根节点 代码 #include<cstdio> #i ...
- BZOJ1455 罗马游戏 左偏树 可并堆
欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ1455 题意概括 n个人,2种操作. 一种是合并两个人团,一种是杀死某一个人团的最弱的人. 题解 左 ...
- BZOJ2333 [SCOI2011]棘手的操作 堆 左偏树 可并堆
欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ2333 题意概括 有N个节点,标号从1到N,这N个节点一开始相互不连通.第i个节点的初始权值为a[i ...
随机推荐
- IOS 缓存思路
为了提高程序的响应速度,可以考虑使用缓存(内存缓存\硬盘缓存) 第一次请求数据时内存缓存中没有数据硬盘缓存中没有数据 当服务器返回数据时,需要做以下步骤 使用服务器的数据(比如解析.显示) 将服务器的 ...
- jq实现多级菜单
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8&quo ...
- javascript编译与运行机理(1)--
随着学习js的深入就越想了解其内部的运行机理,看了很多文章,感觉下面总结的不错.但囿于个人水平,只是达到了理解层面,还不能提出自己的见解,只好把资料罗列出来,留待以后总结: 1.Javascript的 ...
- Codeforces Round #443 (Div. 2) 【A、B、C、D】
Codeforces Round #443 (Div. 2) codeforces 879 A. Borya's Diagnosis[水题] #include<cstdio> #inclu ...
- 自定义控件(视图)2期笔记12:View的滑动冲突之 外部拦截法
1. 外部拦截法: 点击事件通过父容器拦截处理,如果父容器需要就拦截,不需要就不拦截. 这种方法比较符合事件分发机制.外部拦截法需要重写父容器的onInterceptTouchEvent方法,在内部做 ...
- tree视图显示的记录数量
在act_window中,定义limit字段,可以指定打开的tree视图的记录数量. limit:列表视图中每个页面的记录数.
- keras学习笔记2
1.keras的sequential模型需要知道输入数据的shape,因此,sequential的第一层需要接受一个关于输入数据shape的参数,后面的各个层则可以自动的推导出中间数据的shape,因 ...
- 为什么要使用GetSafeHwnd()函数
当我们想得到一个窗口对象(CWnd的派生对象)指针的句柄(HWND)时,最安全的方法是使用GetSafeHwnd()函数,通过下面的例子来看其理由: CWnd *pwnd = FindWindow(“ ...
- [Python]json对象转换出错expected string or buffer python
[问题] 今天在使用python中的json转换碰到一个问题: 错误显示: expected string or buffer json内容如下: {u'err_no': 0, u'corpus_no ...
- 使用Apache HttpClient 4.5设置超时时间
使用HttpClient,一般都需要设置连接超时时间和获取数据超时时间.这两个参数很重要,目的是为了防止访问其他http服务时,由于超时导致自己的应用受影响. 4.5版本中,这两个参数的设置都抽象到了 ...