左偏树
定义一个节点的高度为到叶子节点的最短距离。
一棵左偏树需要满足几个性质:
\(1.\)它是一个堆。
\(2.\)一个节点的左儿子的高度\(\ge\)右儿子的高度。
\(3.\)一个节点的高度\(=\)右儿子的高度\(+1\)。
由此可以得出一个节点数为\(n\)的左偏树的高度为\(\log (n+1)-1\)。
每个节点需要维护左右儿子和权值。
实际上要维护的是左偏树森林,所以同时用并查集维护每个节点所属左偏树的根。
左偏树只有一个核心操作:merge。
merge
merge实现合并两个节点所在的左偏树。
假如我们要合并的两棵左偏树的根是\(u,v\),不妨设\(val_u\)小于\(val_v\)。
然后我们递归合并\(u\)的右儿子和\(v\)。
可以发现我们递归合并之后右儿子的高度最多\(+1\),如果此时右儿子的高度大于左儿子了那就交换左右儿子。
最后维护一下并查集。
int merge(int u,int v)
{
if(!u||!v) return u+v;
if(val[u]>val[v]||(val[u]==val[v]&&u>v)) swap(u,v);
ch[u][1]=merge(ch[u][1],v);
if(h[ch[u][0]]<h[ch[u][1]]) swap(ch[u][0],ch[u][1]);
return fa[ch[u][0]]=fa[ch[u][1]]=fa[u]=u,h[u]=h[ch[u][1]]+1,u;
}
pop
pop实现弹出某个节点所在左偏树的根。
直接把根\(p\)的左右儿子合并起来就好了。
但是因为原左偏树中有些点在并查集中只连到了\(p\),所以要把\(p\)在并查集里连到新的左偏树的根上去。
void pop(int p){val[p]=-1,fa[lc]=lc,fa[rc]=rc,fa[p]=merge(lc,rc);}
原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/12164114.html
时间: 2024-11-04 05:13:34