其实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]左偏树相关推荐

  1. 洛谷P3273 [SCOI2011] 棘手的操作 [左偏树]

    题目传送门 棘手的操作 题目描述 有N个节点,标号从1到N,这N个节点一开始相互不连通.第i个节点的初始权值为a[i],接下来有如下一些操作: U x y: 加一条边,连接第x个节点和第y个节点 A1 ...

  2. 一篇讲左偏树的好文章~

    传送门:http://blog.csdn.net/iaccepted/article/details/6748038 二.左偏树的定义和性质.............................. ...

  3. 可并堆(左高树、左偏树)

    可并堆(左高树.左偏树) 左偏树相对于二叉堆,其插入,弹出,合并的时间复杂度都是对数级别的. 高度优先左偏树(HBLT) 考虑一颗二叉树,将其叶子节点的子节点补全,那么原来有的节点叫做内部节点,新增加 ...

  4. poj 3666 Making the Grade zoj 3512 Financial Fraud 左偏树 or dp

    //poj 3666 //分析:只是在2005年集训队论文黄源河提到的题目上略微有一点点变化 1 #include"iostream" 2 #include"cstdio ...

  5. HDU 5575 Discover Water Tank(左偏树)

    https://vjudge.net/problem/HDU-5575 题意: 有一个水箱,被n-1块板子分成了n个部分,板子的高度不尽相同.现在有m次探测,每次探测在第x部分的y+0.5高度处是否有 ...

  6. 《程序设计解题策略》——1.6 利用左偏树实现优先队列的合并

    本节书摘来自华章计算机<程序设计解题策略>一书中的第1章,第1.6节,作者:吴永辉 王建德 更多章节内容可以访问云栖社区"华章计算机"公众号查看. 1.6 利用左偏树实 ...

  7. 【洛谷3377】 左偏树(可并堆)

    前言 其实我是不小心翻线性基的时候看见的. Solution 左偏树只会模板,挖坑待补 代码实现 #include<stdio.h> #include<stdlib.h> #i ...

  8. 【学习笔记】浅谈短小可爱的左偏树(可并堆)

    文章目录 左偏树 左偏树的合并(merge)操作 例题 罗马游戏 [Apio2012]dispatching [JLOI2015]城池攻占 [Baltic2004]sequence 左偏树 左偏树是一 ...

  9. P3642 [APIO2016]烟火表演(左偏树、函数)

    解析 感觉是左偏树的神题了. 首先有一个比较显然的结论,一个合法的方案中,两个叶子到它们 lca\text{lca}lca 的距离必须相等. 考虑设计 dp\text{dp}dp : fi,xf_{i ...

最新文章

  1. mass Framework pagination插件
  2. 小程序多客服对应售前售后,或者不同的客服人员
  3. shell 工具_Kali Linux渗透工具【八】:系统命令注入漏洞工具 – Commix使用
  4. PHP单引号和双引号的区别
  5. 今天换了ubuntu10.04
  6. java 编译宏_java – 制作一个“宏”命令来运行程序
  7. Hibernate的多表查询,分装到一个新的实体类中的一个方法
  8. 通过数组下标获取值都有哪些方法_通过面试题,让我们来了解Collection
  9. 在Eclipse里使用代码模板
  10. 使用STM32固件库开发GD32 汇总
  11. Mysql调优大全梳理(涵盖90%需要调优的场景)
  12. 打开matlab只在任务栏闪一下然后就没反应了解决办法(亲试有效)
  13. 初学微信云开发,云函数实现两数相加
  14. Redis在手,跟我走
  15. 讯飞智能录音笔SR702让工作体验更佳
  16. 面向工业智能制造的组态系统设计思路与实现
  17. Java File文件流读取文件夹内的文件并替换文件内容
  18. Forever小浮的数学推公式专题题解
  19. 手动搭建redis集群
  20. 如何在ftp服务器编辑文件,ftp服务器怎么编辑文件

热门文章

  1. php word 图片无法显示,Word文档不显示图片解决方法
  2. mysql 触发器条件判断偶尔失效_mysql┃多个角度说明sql优化,让你吊打面试官!...
  3. vscode运行vue时,显示loader. js SyntaxError: Unexpected identifier
  4. python读取txt、csv以及xml文件
  5. 数字游戏---巧妙解答
  6. Java21天打卡Day11-字符串3
  7. Gatling性能测试(一)
  8. 最终,我还是放弃了字节50W开发offer,选择了测试开发……
  9. 为什么说劝人报名IT培训班的人,不是坏就是蠢?
  10. 学考语言成绩c对高考有影响吗_广东2020学考招录办法来了,实现平行志愿,计划不低于年度总计划50%...