【左偏树】【模板】
【左偏树】【模板】
定义
首先定义一个点的dist为:
若该节点是外节点(即左儿子或右儿子为空),则dist=1,否则dist就等于左右儿子dist的较小值+1;
那么左偏树是一个堆的集合,而且满足对于任意一个点都有:右儿子的dist<=左儿子的dist
用途
支持O(log N)的时间复杂度内合并两个堆,其他操作的复杂度与普通的二叉堆相同
实现
1.存储形式
与普通二叉堆的堆式存储不同,左偏树采用与动态开点线段树和平衡树类似的存储方式:
建立一个结构体,左偏树必备的有:左右儿子(ls,rs),dist,key值(val),该节点所在堆的根节点(rt)。除此之外,在不影响左偏树的形态结构的前提下,可以支持对整棵树进行区间加,区间乘,维护相应的懒标记即可。
struct node{
int val,ls,rs,d,rt;
}t[N];
- 查询最值(也就是查找一个点所在堆的根节点)
采用并查集查找的方法,用一个find函数:
int find(int x)
{
return t[x].rt==x?x:t[x].rt=find(t[x].rt);
}
- 合并操作
左偏树的核心操作(设该树为小根堆)
先比较两个堆根节点的大小,设较小的根节点为x,另一个为y。
那么递归合并x的右子树与子树y。
若合并后该节点不满足左偏树的性质:右儿子的dist<=左儿子的dist,则交换左右儿子。
因为在递归的每一层都满足了左偏树的性质,因此,合并后的堆依然是个左偏树。
又因为在递归中每次都用右子树,也就是dist较小的子树进行合并,所以复杂度是不超过O(log N)的。
int merge(int x,int y)
{
if(!x||!y) return x+y;
if(t[x].val>t[y].val||(t[x].val==t[y].val&&x>y)) swap(x,y);
t[x].rs=merge(t[x].rs,y);//t[t[x].rs].rt=x;
if(t[t[x].rs].d>t[t[x].ls].d) swap(t[x].ls,t[x].rs);
t[x].d=t[t[x].rs].d+1;
return x;
}
- 删除操作
若要删除一个堆的根节点,只需将根节点的左右儿子合并即可。
但需要注意的是,因为根节点的维护是利用并查集维护的,因此,若要依然保持整棵树对根节点维护的正确性,不能仅仅把左右儿子的根节点指向合并后原树的根节点,还要把删除的点的根节点指向合并后原树的根节点。这样我们在merge函数中是不涉及rt的操作的。
t[fx].rt=t[t[fx].ls].rt=t[t[fx].rs].rt=merge(t[fx].ls,t[fx].rs);
Tip
虽然理论上要求叶子节点的dist为空,但实际上这不会影响左偏树的正确性,而这对左偏树的复杂度也影响极小,所以实际上是否初始将叶节点的dist设为1,或者将0节点的dist设为-1,影响其实不大。
Code
#include<bits/stdc++.h>
using namespace std;
//#define int long long
inline int read()
{
register int x=0,w=1;
register char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if(ch=='-') {ch=getchar();w=-1;}
while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*w;
}
inline void write(int x)
{
if(x<0) putchar('-'),x=~(x-1);
if(x>9) write(x/10);
putchar('0'+x%10);
}
const int N=1e5+100;
int n,m,vis[N];
struct node{
int ls,rs,val,rt,d;
}t[N];
int merge(int x,int y)
{
if(!x||!y) return x+y;
if(t[x].val>t[y].val) swap(x,y);
if(t[x].val==t[y].val&&y<x) swap(x,y);
t[x].rs=merge(t[x].rs,y);
if(t[t[x].ls].d<t[t[x].rs].d) swap(t[x].ls,t[x].rs);
t[x].d=t[t[x].rs].d+1;//t[t[x].ls].rt=t[t[x].rs].rt=t[x].rt=x;
return x;
}
int find(int x)
{
return t[x].rt==x?x:t[x].rt=find(t[x].rt);
}
signed main()
{
// freopen("P3377_11.in","r",stdin);freopen("my.out","w",stdout);
n=read();m=read();
for(int i=1;i<=n;++i){
t[i].val=read();t[i].rt=i;//t[i].d=1;
}
for(int i=1;i<=m;++i)
{
int op=read();
if(op-1){
int x=read();
if(vis[x]){
puts("-1");continue;
}
int fa=find(x);vis[fa]=1;write(t[fa].val);puts("");//x=fa;
// t[t[x].ls].rt=t[x].ls;t[t[x].rs].rt=t[x].rs;t[x].rt=merge(t[x].ls,t[x].rs);
t[fa].rt=t[t[fa].ls].rt=t[t[fa].rs].rt=merge(t[fa].ls,t[fa].rs);
}
else{
int x=read(),y=read();
if(vis[x]||vis[y]) continue;
int fx=find(x),fy=find(y);
if(fx==fy) continue;
t[fx].rt=t[fy].rt=merge(fx,fy);
}
}
return 0;
}

浙公网安备 33010602011771号