大佬们请先对并查集略知一二。
今日,学习了传说中的左偏树(虽然感觉学的还是假的)
学习地址:https://www.luogu.org/blog/IAmHellWord/solution-p3377
附加丑代码一段
#include <bits/stdc++.h>
using namespace std;
#define C getchar()
#define maxn 101000
inline int read()
{
int x=0;
char ch;
bool flag=true;
while(!isdigit(ch))
{
if(ch=='-')
flag=false;
ch=C;
}
while(isdigit(ch))
{
x=(x<<3)+(x<<1)+(ch^48);
ch=C;
}
return flag?x:-x;
}
struct lefttree
{
int lson;
int rson;
int key;
int fa;
int dis;
}tree[maxn]={};
int n,m;
int Un(int u,int v){
if (!u || !v)return u+v;
if (tree[u].key>tree[v].key || (tree[u].key==tree[v].key && u>v))swap(u,v);
int &ul=tree[u].lson,&ur=tree[u].rson;
ur=Un(ur,v);
tree[ur].fa=u;
if (tree[ul].dis<tree[ur].dis)swap(ul,ur);
tree[u].dis=tree[ur].dis+1;
return u;
}
void pop(int n)
{
int ul=tree[n].lson,ur=tree[n].rson;
tree[n].key=-1,tree[ul].fa=0,tree[ur].fa=0;
Un(ul,ur);
}
int gf(int x)
{
return tree[x].fa?gf(tree[x].fa):x;
}
void intt()
{
n=read();
m=read();
tree[0].dis=-1;
}
void work()
{
for(int i=1;i<=n;i++)
tree[i].key=read();
for(int i=1;i<=m;i++)
{
int opt,x,y;
opt=read();
x=read();
if(opt==1)
{
y=read();
if(tree[x].key!=-1&&tree[y].key!=-1)
{
int p=gf(x);
int q=gf(y);
if(q!=p)
Un(p,q);
}
}
else
{
if(tree[x].key==-1)
printf("%d\n",-1);
else
{
int p=gf(x);
printf("%d\n",tree[p].key);
pop(p);
}
}
}
}
int main()
{
intt();
work();
return 0;
}
以前仗着有stl库中的堆,打死不学左偏树(反正左偏树除了支持合并以外好像与堆并没有差别),直到碰到了
https://www.luogu.org/problemnew/show/P3377
整个人都不好了。
然后就只有找资料认真学习了,but,为神马网上的blog用的都是指针呢,咱老师说了,用指针的同学特别容易翻车,所以本人就毅然决然地放弃了指针的做法,找到了这么一篇良心题解。
下面就请允许我浅谈一下对左偏树的理解
左偏树,又称可并二叉堆,是基于合并操作的一种数据结构
好了,那么左偏树为什么叫左偏树呢,因为他看起来是向左偏的(我也不知道为什么不规定叫右偏树)
因为他的这一个基本性质,所以也就具备了一下三种特性(以小根堆为例)
1.爸爸节点的权值一定小于等于其左右儿子的权值。
2.左儿子的dis(距离)大于右儿子的dis(dis是此节点到最近叶子结点的距离,即要经过的边的条数)。
3.爸爸节点的dis等于右儿子的dis+1。(废话)
然后根据三个结论,很容易就可以推出一个引理
一个节点个数为n的左偏书的最大距离为log(n+1)-1。
以上便是左偏树的一些基本性质
下面就来讲一些基本操作吧
第一:合并(代码详见上文自定义函数Un)
这是左偏树操作中最为重要的一个。
他的具体方法为将两棵树一深度较大的那一颗为母树,再将另一棵树合并到母树的右儿子上,然后检查左偏性质,如不满足,交换左右子树,强行使其满足左偏性质。此时由于右儿子可能改变,所以爸爸的深度要改为右儿子的深度+1.
第二:删除最小数(代码详见自定义函数pop)
删除最小数其实就相当于将左右儿子合并,这一个操作是等价于删除爸爸节点的。
第三:查找最小数(代码详见gf)
这里便用到了并查集的思想,由于左偏树的性质一,所以根节点的权值便是最小数。
以上三个蛇皮操作都显示出了左偏树一合并为核心操作的性质,也显示出了左偏树对并查集的依赖性。
本文就这么草草地结束了,欢迎各位dalao在评论区留言指出错误之处或改进建议
转载请标明出处(我相信不会有人转载的)