左偏树学习笔记
今天刚刚学习了左偏树,这里记录一下,加深理解
首先,我们用左偏树可以做到\(O(\log_n)\)来合并两个堆,\(O(\log_n)\)来删除等一些\(O(\log_n)\)的操作
设\(dis\)为这个节点到它的子树中最近的一个叶子的距离,对于一个小根堆它有这么几条性质
1.一个节点的值小与左、右儿子的值
2.一个节点的左儿子的\(dis\)不小于右儿子的\(dis\)
3.一个节点的\(dis\)始终等于右儿子+1
4.节点数为n的左偏树,距离最大为\(\log_{(n+1)}−1\)
然后来写一点它的基本的操作
\(Merge\)合并堆
每次递归的\(x\)和\(y\)就是两个堆的根节点,首先我们要让\(y\)的值更大来方便后续的操作,判一下是不是\(y\)更大,不是就\(swap\)一下,然后我们就可以考虑让\(y\)所在的堆来与\(x\)的右子树合并,然后一直递归,最后把\(y\)接到\(x\)的右子树上
那么怎么维护左偏呢?每次判一下\(x\)的左右两个儿子,如果右儿子的\(dis\)大于左儿子的,那么交换这两个儿子
int merge(int xx,int yy)
{
if(xx==0||yy==0)
return max(xx,yy);
if(a[xx].num>a[yy].num||(a[xx].num==a[yy].num&&xx>yy))
swap(xx,yy);
a[xx].r1=merge(a[xx].r1,yy);
if(a[a[xx].l1].dis<a[a[xx].r1].dis)
swap(a[xx].l1,a[xx].r1);
a[xx].dis=a[a[xx].r1].dis+1,a[a[xx].r1].fa=a[a[xx].l1].fa=a[xx].fa=xx;
return xx;
}
Pop删除所在堆中最小结点
这个就只接把堆顶的左右两个子树\(Merge\)一下即可
但是有一个问题让我在这里想了很久,代码调了好久才发现是这里的问题,这也是我写这篇博客的理由QWQ
a[xx].fa=merge(a[xx].l1,a[xx].r1);
如果你是写的路径压缩的并查集,那么就要把删去的点的父亲设为他的两个子树合并后的堆顶
那么这是为什么呢?如上图所示,我们删去了1号节点,而由于路径压缩的原因,下面的2、3、4、5号节点的父亲仍为1号节点,如果不把1号节点的父亲设为2号节点,那么在查找下面节点的堆顶的时候就会找到1号节点这个已经被删掉的节点
void pop(int xx)
{
a[xx].num=-1,a[a[xx].l1].fa=a[xx].l1,a[a[xx].r1].fa=a[xx].r1;
a[xx].fa=merge(a[xx].l1,a[xx].r1);
}
还有一些其他的操作,要注意一下维护左偏树的性质,具体就不在这写了
洛谷模板的代码
#include<bits/stdc++.h>
using namespace std;
struct node
{
int num,l1,r1,fa,dis;
}a[100003];
int n,m,typ,x,y;
int get(int x1)
{
while(a[x1].fa!=x1)
a[x1].fa=a[a[x1].fa].fa,x1=a[x1].fa;
return x1;
}
int merge(int xx,int yy)
{
if(xx==0||yy==0)
return max(xx,yy);
if(a[xx].num>a[yy].num||(a[xx].num==a[yy].num&&xx>yy))
swap(xx,yy);
a[xx].r1=merge(a[xx].r1,yy);
if(a[a[xx].l1].dis<a[a[xx].r1].dis)
swap(a[xx].l1,a[xx].r1);
a[xx].dis=a[a[xx].r1].dis+1,a[a[xx].r1].fa=a[a[xx].l1].fa=a[xx].fa=xx;
return xx;
}
void pop(int xx)
{
a[xx].num=-1,a[a[xx].l1].fa=a[xx].l1,a[a[xx].r1].fa=a[xx].r1;
a[xx].fa=merge(a[xx].l1,a[xx].r1);
}
int main()
{
scanf("%d%d",&n,&m);
a[0].dis=-1;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
a[i].fa=i;
}
for(int i=1;i<=m;i++)
{
scanf("%d%d",&typ,&x);
if(typ==1)
{
scanf("%d",&y);
if(a[x].num==-1||a[y].num==-1)
continue;
x=get(x),y=get(y);
if(x!=y)
a[x].fa=a[y].fa=merge(x,y);
}
else
{
if(a[x].num==-1)
{
cout<<-1<<endl;
continue;
}
x=get(x);
cout<<a[x].num<<endl;
pop(x);
}
}
return 0;
}