[模板] 左偏树
左偏树
用途
支持\(O(\log n)\)合并,\(O(\log n)\)删除堆顶,\(O(\log n)\)查找,\(O(1)\)取最小值的堆.
定义
对于左偏树中的一个节点x,离它最近的一个叶子结点经过的边数称为它的距离,记为 \(dist(x)\) .特别地,外结点的距离为0,空节点(null)的距离为-1.
对于左偏树中的任意节点,满足它的左子树的距离大于等于右子树的距离.
代码
int val[nsz];
struct tnd{int v,l,r,d,f;}tree[nsz];
void init(){rep(i,1,n)tree[i].v=val[i];}
int merge(int a,int b){//b -> a
if(a==0||b==0)return a+b;
if(tree[a].v>tree[b].v||(tree[a].v==tree[b].v&&a>b))swap(a,b);
tree[a].r=merge(tree[a].r,b);
tree[tree[a].r].f=a;
if(tree[tree[a].l].d<tree[tree[a].r].d)swap(tree[a].l,tree[a].r);
tree[a].d=tree[tree[a].r].d+1;
return a;
}
int getf(int x){
while(tree[x].f)x=tree[x].f;
return x;
}
void del(int x){
val[x]=-1;
tree[tree[x].l].f=tree[tree[x].r].f=0;
merge(tree[x].l,tree[x].r);
}
//merge two heaps
if(val[a]==-1||val[b]==-1||a==b)continue;
a=getf(a),b=getf(b);
if(a==b)continue;
merge(getf(a),getf(b));
//delete top
if(val[a]==-1)continue;
else a=getf(a),cout<<val[a]<<'\n',del(a);
题
随机偏堆
每次合并的时候, 不需要判断两边的深度或者大小, 直接 rand()
得到要合并的儿子.
容易发现期望意义下复杂度是正确的, 与左偏树相同.
炒鸡好写...
int merge(int u,int v){
int p=val[u]>val[v]?u:v,x=rand()&1;
return (u&&v)?(ch[p][x]=merge(ch[p][x],u+v-p),p):(u|v);
}