码迷,mamicode.com
首页 > 其他好文 > 详细

堆(左偏树)

时间:2020-01-08 00:30:40      阅读:66      评论:0      收藏:0      [点我收藏+]

标签:核心   span   操作   cpp   左偏树   swa   个性   line   code   

左偏树

定义一个节点的高度为到叶子节点的最短距离。
一棵左偏树需要满足几个性质:
\(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);}

堆(左偏树)

标签:核心   span   操作   cpp   左偏树   swa   个性   line   code   

原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/12164114.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!