因为也是昨天刚接触左偏树,从头理解,如有不慎之处,跪请指教。

左偏树:

什 么是(fzy说)左偏树啊?

前置知识:

  左偏树中dist:表示到右叶点(就是一直往右下找,最后一个)的距离,特别的,无右节点的为0。

  堆:左偏树是个堆。

  关于左偏性质:可以帮助堆合并(研究深了我也不懂的,看代码理解)

  对于任意的节点,dist[leftson]>=dist[rightson],体现了左偏性质。

  同理可得:对于任意右儿子的父亲节点的dist自然等于右儿子的dist+1喽

关于各种操作:

merge:

  是插入操作的函数,具体步骤如下:

  1.对于两个堆x,y,判断x,y是否为0,如果有一个为0,相当于没合并,直接返回另一个有元素的堆。

  2.找到value值更大的那个堆头放到顶上,如果value值一样的话就按堆顶编号来排序。为了方便代码实现,我们可以规定x为符合条件(小,大跟堆)的那个堆头,然后如果y符合条件就交换x,y值。

  3.既然堆头找着了,就可以进一步的合并堆头右儿子和y堆了(为了尽量保证左偏的性质)。如此递归下去,随着新堆头被一次次确定,最终这个堆会被一点一点融合到另一个堆中。

  4.但是,鉴于合成完后,不一定能够保证左子树的dist值一定会比右字数的大,我们只要判断一下是否符合左偏性质,如果不符合,就交换一下当前节点左右子树就行了。因为是递归执行,从更深节点一层一层上来,那么必然的整个堆会符合左偏性质。然后更新一下dist为右子树dist+1.一次merge完成。

代码:

inline int merge(int x, int y)
{
if(!x||!y)return x+y;
if(tree[x].value>tree[y].value||(tree[x].value==tree[y].value&&x>y))swap(x,y);
rs=merge(rs,y);
if(tree[ls].dist<tree[rs].dist)swap(ls, rs);tree[ls].rt=tree[rs].rt=tree[x].rt=x,tree[x].dist=tree[rs].dist+;
//更新dist
return x ;
}

2.pop弹出函数:

弹出函数,即弹出堆顶。方法很简单:没有了堆顶,整个左偏树就被分为了两个小的左偏树。我们只要忽略掉堆顶合并(merge)两个小的左偏树即可。

注意事项:不要忘了堆顶元素相关信息还原为初始。

代码:

inline void pop(int x)//弹出x为堆顶的堆
{
tree[x].value=-,tree[ls].rt=ls,tree[rs].rt=rs;
tree[x].rt=merge(ls,rs);
}

3.get函数:

没啥可说的,就是并查集找父亲并且路径压缩。

代码:

int get(int x)
{
return x==tree[x].rt?x:tree[x].rt=get(tree[x].rt);
}

三个函数代码已经完结。

main函数内根据题意进行模拟即可。

总代码:

#include<queue>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#define N 100003
#define ls tree[x].son[0]
#define rs tree[x].son[1]
using namespace std;
int read()
{
int ans=;
char ch=getchar(),last=' ';
while(ch<''||ch>'')last=ch,ch=getchar();
while(ch>=''&&ch<='')ans=(ans<<)+(ans<<)+ch-'',ch=getchar();
return last=='-'?-ans:ans;
}
inline void swap(int &x,int &y)
{
x^=y^=x^=y;
}
int n,num,hea[N],t,judge,b,c;
struct tre{
int son[],rt,dist,value;
}tree[N];
inline int merge(int x, int y)
{
if(!x||!y)return x+y;
if(tree[x].value>tree[y].value||(tree[x].value==tree[y].value&&x>y))swap(x,y);
rs=merge(rs,y);
if(tree[ls].dist<tree[rs].dist)swap(ls, rs);tree[ls].rt=tree[rs].rt=tree[x].rt=x,tree[x].dist=tree[rs].dist+;
//更新dist
return x ;
}
int get(int x)
{
return x==tree[x].rt?x:tree[x].rt=get(tree[x].rt);
}
inline void pop(int x)//弹出x为堆顶的堆
{
tree[x].value=-,tree[ls].rt=ls,tree[rs].rt=rs;
tree[x].rt=merge(ls,rs);
}
int main(){
n=read(),t=read();tree[].dist=-;
for (int i=;i<=n;i++)
tree[i].rt=i,scanf("%d",&tree[i].value);//并差集初始化+输入
for (int i=;i<=t;i++){
judge=read(),b=read();
if (judge==){
c=read();
if (tree[b].value==-||tree[c].value==-) continue ;
int f1=get(b),f2=get(c);if(f1!=f2)tree[f1].rt=tree[f2].rt=merge(f1,f2);//合并操作
}
else {
if(tree[b].value==-)printf("-1\n") ;
else printf("%d\n",tree[get(b)].value),pop(get(b)) ;//输出并弹出
}
}
return ;
}

完结。

彩蛋:有趣的东西:

极度真实的左偏树。

来自dalao

P3377 【模板】左偏树(可并堆) 左偏树浅谈的更多相关文章

  1. 【BZOJ 2333 】[SCOI2011]棘手的操作(离线+线段树|可并堆-左偏树)

    2333: [SCOI2011]棘手的操作 Description 有N个节点,标号从1到N,这N个节点一开始相互不连通.第i个节点的初始权值为a[i],接下来有如下一些操作: U x y: 加一条边 ...

  2. 浅谈Java中的栈和堆

    人们常说堆栈堆栈,堆和栈是内存中两处不一样的地方,什么样的数据存在栈,又是什么样的数据存在堆中? 这里浅谈Java中的栈和堆 首先,将结论写在前面,后面再用例子加以验证. Java的栈中存储以下类型数 ...

  3. [note]左偏树(可并堆)

    左偏树(可并堆)https://www.luogu.org/problemnew/show/P3377 题目描述 一开始有N个小根堆,每个堆包含且仅包含一个数.接下来需要支持两种操作: 操作1: 1 ...

  4. Monkey King(左偏树 可并堆)

    我们知道如果要我们给一个序列排序,按照某种大小顺序关系,我们很容易想到优先队列,的确很方便,但是优先队列也有解决不了的问题,当题目要求你把两个优先队列合并的时候,这就实现不了了 优先队列只有插入 删除 ...

  5. bzoj2809 [Apio2012]dispatching——左偏树(可并堆)

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2809 思路有点暴力和贪心,就是 dfs 枚举每个点作为管理者: 当然它的子树中派遣出去的忍者 ...

  6. 浅谈左偏树在OI中的应用

    Preface 可并堆,一个听起来很NB的数据结构,实际上比一般的堆就多了一个合并的操作. 考虑一般的堆合并时,当我们合并时只能暴力把一个堆里的元素一个一个插入另一个堆里,这样复杂度将达到\(\log ...

  7. 笔试算法题(46):简介 - 二叉堆 & 二项树 & 二项堆 & 斐波那契堆

    二叉堆(Binary Heap) 二叉堆是完全二叉树(或者近似完全二叉树):其满足堆的特性:父节点的值>=(<=)任何一个子节点的键值,并且每个左子树或者右子树都是一 个二叉堆(最小堆或者 ...

  8. 洛谷——P3919 【模板】可持久化数组(可持久化线段树/平衡树)

    P3919 [模板]可持久化数组(可持久化线段树/平衡树) 题目背景 UPDATE : 最后一个点时间空间已经放大 标题即题意 有了可持久化数组,便可以实现很多衍生的可持久化功能(例如:可持久化并查集 ...

  9. 浅谈树形结构的特性和应用(上):多叉树,红黑树,堆,Trie树,B树,B+树...

    上篇文章我们主要介绍了线性数据结构,本篇233酱带大家康康 无所不在的非线性数据结构之一:树形结构的特点和应用. 树形结构,是指:数据元素之间的关系像一颗树的数据结构.我们看图说话: 它具有以下特点: ...

随机推荐

  1. Leetcode之动态规划(DP)专题-474. 一和零(Ones and Zeroes)

    Leetcode之动态规划(DP)专题-474. 一和零(Ones and Zeroes) 在计算机界中,我们总是追求用有限的资源获取最大的收益. 现在,假设你分别支配着 m 个 0 和 n 个 1. ...

  2. Hyperledger Fabric 常用命令

    Peer常用命令: #peer chaincode --help #peer channel list --help --logging-level <string> #<strin ...

  3. 【计算机视觉】深度相机(九)--OpenNI API及中间件说明

    本文由官方文档翻译而来 总览 目的 OpenNI 2.0 API(应用程序编程接口)提供了访问PrimerSense的兼容深度传感器.这就使得一个应用程序能够初始化传感器和从设备接收深度(depth) ...

  4. Optional的理解和使用

    1.Optional 到底是什么? Optional 是一个包装类.类中包装的对象可以为 NULL 或非 NULL.简单说就是把 NULL 包了一层,防止直接对 NULL 操作报NPE. 2.Opti ...

  5. Springboot 使用Jwt token失效时接口无响应(乌龙)

    问题背景:新项目使用Springboot框架,鉴权使用了Jwt 处理cors: @Configuration public class WebMvcConfig implements WebMvcCo ...

  6. Linux增加swap空间大小解决编译错误collect2:ld terminated with signal 9(或11)

    编译llvm的时候:collect2:ld terminated with signal 9(或11) 1 Creating a file for 1024 MB size you want: We ...

  7. ssh出现公钥错误问题的解决方法

      问题:主机app1推送公钥时,公钥判定错误   原因:之前推过公钥,用的是ip而不是主机名(即hosts文件中的对应关系不对),导致app1的~/.ssh/known_hosts中的公钥对不上. ...

  8. Spring4学习回顾之路12-事务

    事务:事务就是一系列的动作,它们被当做一个单独的工作单元,这些动作要么全部完成,要么全部不起作用:事务管理是企业级应用程序开发中必不可少的技术,用来确保数据的完整性和一致性.事务的四个关键属性(ACI ...

  9. (三)IDEA创建Spring项目

    新建项目的时候,选择Spring : 在Spring的下面,有许多选项,根据自己需求选择,我是初学,就一个都没有勾选: 选择 Web Application 选项 默认是下载Spring的jar包:如 ...

  10. ide的debug

    webstom 新建立一个配置项 找到webpack.config.js,最后一行加上 devtool: "source-map" 然后点击debug