[2018.12.6]左偏树
其实NOIp之前就学会了...结果咕到了现在...
我们都知道堆。但是很少有人会手写堆。因为我们有STL,而且手写堆码量不小(据说是吧?没写过)。
而且堆的\(Merge\)操作又慢又麻烦。
于是就有了可并堆。
即使c++也有自带的可并堆
左偏树就是其中之一。
Luogu模板题链接
什么是左偏树
就是一颗向左偏的树。(逃
看一个例子:
(来源:百度图片)
好像确实左偏...
左偏树中,定义一个外节点为:左孩子或者右孩子为空的节点。
而一个节点的距离为它的子树中与它最近的外节点的距离。
外节点的距离为0,空节点的距离为-1(方便计算)。
距离在图中用蓝色字体标出。
接下来的内容中,我们维护大根堆(小根堆类似),对于左偏树的一个节点,其定义如下:
struct Ltree{//丑陋的结构体名int v,d,f,c[2];//v:节点的值 d:距离 f:父亲 c[0]:左孩子 c[1]:右孩子
}t[Size];//丑陋的变量名
左偏树的性质
左偏性质(不然干嘛叫左偏树):
对于任意节点\(x\),有\(t_{t_x.c_0}.d\ge t_{t_x.c_1}.d\),说人话就是一个节点的左孩子的距离大于它右孩子的距离。
这个性质保证左偏树的时间复杂度(介绍合并操作的时候会让大家感性理解这一点)。
堆性质(不然干嘛叫可并堆):
对于任意节点\(x\),有\(t_x.v\ge t_{t_x.c_0}.v\),\(t_x.v\ge t_{t_x.c_1}.v\),说人话就是任何一个节点的值大于等于它所有孩子的值。
这个性质保证左偏树的正确性。
左偏树的操作
注意:如果像模板题中一样需要判断是否被删除,需要另行记录。
找根操作(\(Find(x)\))
找到\(x\)节点的根。
不停跳\(t_x.f\)即可。
code:
int Find(int x){while(t[x].f)x=t[x].f;return x;
}
合并操作(\(Merge(x,y)\))
将以\(x,y\)为根的堆并在一起。
由于要维护堆性质,合并它们时,要让值大的在上面。
我们假设\(x\)为根,那么如果\(t_x.v<t_y.v\),就交换\(x\)和\(y\)。
然后呢?不用管然后了,直接递归到\(Merge(t_x.c_1,y)\)即可。
注意这里我们把\(y\)和\(t_x.c_1\)合并,而不是\(t_x.c_0\),就是因为左偏性质,右边的距离更小,在右边进行合并可以维持它的平衡,确保复杂度。(你要我严谨证明我也不会啊...)
然后此时\(t_x.c_0\)的距离有可能小于\(t_x.c_1\),如果出现这种情况,交换\(x\)的左右儿子。
然后需要更新\(x\)的距离。
显然\(t_x.d=t_{t_x.c_1}.d+1\)。应该很容易理解。
code:
void Merge(int &x,int &y){if(!y)return;//结束if(!x){//结束swap(x,y);return;}if(t[x].v<t[y].v)swap(x,y);//维护堆性质,交换x,yMerge(t[x].c[1],y);t[t[x].c[1]].f=x;//更新右孩子的父亲if(t[t[x].c[0]].d<t[t[x].c[1]].d)swap(t[x].c[0],t[x].c[1]);//维护左偏性质,交换x的左右孩子t[x].d=t[t[x].c[1]].d+1;//更新距离
}
删除操作(\(Delete(x)\))
删除节点\(x\)所在堆的最大值。
找到\(x\)所在左偏树的根,将根的左右孩子的父亲设为空,合并它们即可。
code:
void Delete(int x){t[t[x].c[0]].f=t[t[x].c[1]].f=0;Merge(t[x].c[0],t[x].c[1]);
}
Upd(2018.12.12)
才知道删除还可以删除任意已知节点。。。
这里的已知指我们可以直接访问它的位置,而不是知道它的值。比如左偏树不能完成例如删除树中值为233的节点,但是可以进行例如删除编号为233的节点(我们可以直接访问233号节点)。
类似删除根的方法,我们先合并它的左右子树,然后一直跳它的父亲:
当他父亲的距离等于新合并的子树的距离+1,就可以停止了;
当它的父亲的距离大于新合并的子树的距离+1,更新父亲的距离,如果这棵子树是父亲的左子树的话还需要交换父亲的子树;
当它的父亲的距离小于新合并的子树的距离+1,如果这棵子树是父亲的左子树就停止,否则更新父亲的距离为两棵子树距离的较小值+1,如果左子树的距离更小就交换左子树。
因为作者很懒所以代码不给出。
最值操作(\(Max(x)\))
返回\(x\)所在左偏树的最大值。
也就是返回根的值啦!
code:
int Max(int x){return t[Find(x)].v;
}
没了。
这个数据结构是如此的简单。
还有一点要说的:关于给一个序列建左偏树。
给每个元素建一棵一个节点的左偏树,顺序合并即可。时间复杂度\(O(nlogn)\)。
讲完了。
最后附上模板题代码:
#include<bits/stdc++.h>
using namespace std;
struct Pair{int v,id;bool operator >(Pair y){if(v!=y.v)return v>y.v;return id>y.id;}bool operator <(Pair y){if(v!=y.v)return v<y.v;return id<y.id;}
};
struct node{int f,d,c[2];Pair v;
}t[100010];
int n,m,op,u,v,fu,fv,del[100010];
int getf(int x){while(t[x].f)x=t[x].f;return x;
}
void Merge(int &x,int &y){if(!y)return;if(!x){swap(x,y);return;}if(t[x].v>t[y].v){swap(x,y);}Merge(t[x].c[1],y);t[t[x].c[1]].f=x;if(t[t[x].c[1]].d>t[t[x].c[0]].d){swap(t[x].c[1],t[x].c[0]);}t[x].d=t[t[x].c[1]].d+1;
}
int Delete(int x){t[t[x].c[0]].f=t[t[x].c[1]].f=0;Merge(t[x].c[0],t[x].c[1]);del[x]=1;return t[x].v.v;
}
int main(){scanf("%d%d",&n,&m);t[0].d=-1;for(int i=1;i<=n;i++){scanf("%d",&t[i].v.v);t[i].v.id=i;t[i].d=0;}for(int i=1;i<=m;i++){scanf("%d",&op);if(op==1){scanf("%d%d",&u,&v);fu=getf(u);fv=getf(v);if(del[u]||del[v]||fu==fv)continue;Merge(fu,fv);}else{scanf("%d",&u);if(del[u]){puts("-1");}else{fu=getf(u);printf("%d\n",Delete(fu));}}}return 0;
}
练习题:
[APIO2012]派遣
->Luogu
->BZOJ
->题解
转载于:https://www.cnblogs.com/xryjr233/p/10536872.html
[2018.12.6]左偏树相关推荐
- 洛谷P3273 [SCOI2011] 棘手的操作 [左偏树]
题目传送门 棘手的操作 题目描述 有N个节点,标号从1到N,这N个节点一开始相互不连通.第i个节点的初始权值为a[i],接下来有如下一些操作: U x y: 加一条边,连接第x个节点和第y个节点 A1 ...
- 一篇讲左偏树的好文章~
传送门:http://blog.csdn.net/iaccepted/article/details/6748038 二.左偏树的定义和性质.............................. ...
- 可并堆(左高树、左偏树)
可并堆(左高树.左偏树) 左偏树相对于二叉堆,其插入,弹出,合并的时间复杂度都是对数级别的. 高度优先左偏树(HBLT) 考虑一颗二叉树,将其叶子节点的子节点补全,那么原来有的节点叫做内部节点,新增加 ...
- poj 3666 Making the Grade zoj 3512 Financial Fraud 左偏树 or dp
//poj 3666 //分析:只是在2005年集训队论文黄源河提到的题目上略微有一点点变化 1 #include"iostream" 2 #include"cstdio ...
- HDU 5575 Discover Water Tank(左偏树)
https://vjudge.net/problem/HDU-5575 题意: 有一个水箱,被n-1块板子分成了n个部分,板子的高度不尽相同.现在有m次探测,每次探测在第x部分的y+0.5高度处是否有 ...
- 《程序设计解题策略》——1.6 利用左偏树实现优先队列的合并
本节书摘来自华章计算机<程序设计解题策略>一书中的第1章,第1.6节,作者:吴永辉 王建德 更多章节内容可以访问云栖社区"华章计算机"公众号查看. 1.6 利用左偏树实 ...
- 【洛谷3377】 左偏树(可并堆)
前言 其实我是不小心翻线性基的时候看见的. Solution 左偏树只会模板,挖坑待补 代码实现 #include<stdio.h> #include<stdlib.h> #i ...
- 【学习笔记】浅谈短小可爱的左偏树(可并堆)
文章目录 左偏树 左偏树的合并(merge)操作 例题 罗马游戏 [Apio2012]dispatching [JLOI2015]城池攻占 [Baltic2004]sequence 左偏树 左偏树是一 ...
- P3642 [APIO2016]烟火表演(左偏树、函数)
解析 感觉是左偏树的神题了. 首先有一个比较显然的结论,一个合法的方案中,两个叶子到它们 lca\text{lca}lca 的距离必须相等. 考虑设计 dp\text{dp}dp : fi,xf_{i ...
最新文章
- mass Framework pagination插件
- 小程序多客服对应售前售后,或者不同的客服人员
- shell 工具_Kali Linux渗透工具【八】:系统命令注入漏洞工具 – Commix使用
- PHP单引号和双引号的区别
- 今天换了ubuntu10.04
- java 编译宏_java – 制作一个“宏”命令来运行程序
- Hibernate的多表查询,分装到一个新的实体类中的一个方法
- 通过数组下标获取值都有哪些方法_通过面试题,让我们来了解Collection
- 在Eclipse里使用代码模板
- 使用STM32固件库开发GD32 汇总
- Mysql调优大全梳理(涵盖90%需要调优的场景)
- 打开matlab只在任务栏闪一下然后就没反应了解决办法(亲试有效)
- 初学微信云开发,云函数实现两数相加
- Redis在手,跟我走
- 讯飞智能录音笔SR702让工作体验更佳
- 面向工业智能制造的组态系统设计思路与实现
- Java File文件流读取文件夹内的文件并替换文件内容
- Forever小浮的数学推公式专题题解
- 手动搭建redis集群
- 如何在ftp服务器编辑文件,ftp服务器怎么编辑文件
热门文章
- php word 图片无法显示,Word文档不显示图片解决方法
- mysql 触发器条件判断偶尔失效_mysql┃多个角度说明sql优化,让你吊打面试官!...
- vscode运行vue时,显示loader. js SyntaxError: Unexpected identifier
- python读取txt、csv以及xml文件
- 数字游戏---巧妙解答
- Java21天打卡Day11-字符串3
- Gatling性能测试(一)
- 最终,我还是放弃了字节50W开发offer,选择了测试开发……
- 为什么说劝人报名IT培训班的人,不是坏就是蠢?
- 学考语言成绩c对高考有影响吗_广东2020学考招录办法来了,实现平行志愿,计划不低于年度总计划50%...