配对堆是一种可并堆
题意:两种操作,合并两个堆或者查询一个堆的最小值,n≤106n\leq 10^6n≤106
Pairing−HeapPairing-HeapPairing−Heap还挺好写的,不过并没有传说中那么快
这里没有Decrease−KeyDecrease-KeyDecrease−Key的代码,说一下大概怎么实现
额外记录一个FatherFatherFather域,执行Decrease−KeyDecrease-KeyDecrease−Key的时候,把该节点xxx的子树抽出来(只是将FatherxFather_xFatherx设为000而不变动FatherxFather_xFatherx的结构)
让后和根节点合并,当Pop−MinPop-MinPop−Min的时候,如果发现一个儿子的FatherFatherFather不是自己那么就不把他加入合并的队列中,这样不影响时间复杂度
时间复杂度,除了Pop−MinPop-MinPop−Min都是O(1)O(1)O(1)的,Pop−MinPop-MinPop−Min应该是均摊O(log n)O(\log\ n)O(log n)
这玩意比左偏树还好写,也比左偏树快
#include<stdio.h>
#define N 1000010
#define mid (l+r>>1)
using namespace std;
int n,m,b[N],c,f[N],d[N],rt[N];
struct tree{ int v,x,s,r; } s[N];
inline int gf(int x){
for(;x^f[x];x=f[x]=f[f[x]]); return x;
}
inline int merge(int x,int y){
if(s[x].v>s[y].v) x^=y^=x^=y;
s[y].r=s[x].s; s[x].s=y; s[x].r=0; return x;
}
inline int cdq(int l,int r){
if(l==r) return b[l];
return merge(cdq(l,mid),cdq(mid+1,r));
}
inline int pop(int x){
if(!s[x].s) return 0; c=0;
for(int y=s[x].s;y;y=s[y].r) b[++c]=y;
return cdq(1,c);
}
int main(){
scanf("%d%d",&n,&m);
for(int x,i=1;i<=n;++i){
scanf("%d",&x); s[i]=(tree){x,i,0,0}; rt[i]=f[i]=i;
}
for(int o,x,y;m--;){
scanf("%d%d",&o,&x);
if(o>1){
if(d[x]){ puts("-1"); continue; }
x=gf(x); printf("%d\n",s[rt[x]].v);
d[s[rt[x]].x]=1; rt[x]=pop(rt[x]);
} else {
scanf("%d",&y);
if(d[x] || d[y]) continue;
x=gf(x); y=gf(y);
if(x!=y){
f[y]=x; rt[x]=merge(rt[x],rt[y]);
}
}
}
}

配对堆是一种可并堆,适用于合并堆和查询最小值操作。在没有Decrease-Key操作的情况下,配对堆的代码实现相对简单,且除Pop-Min外的操作时间复杂度为O(1),Pop-Min的均摊时间复杂度为O(log n)。配对堆相比左偏树更易写且速度更快。

1281

被折叠的 条评论
为什么被折叠?



