配对堆模板

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

配对堆是一种可并堆

题意:两种操作,合并两个堆或者查询一个堆的最小值,n≤106n\leq 10^6n106

Pairing−HeapPairing-HeapPairingHeap还挺好写的,不过并没有传说中那么快
这里没有Decrease−KeyDecrease-KeyDecreaseKey的代码,说一下大概怎么实现
额外记录一个FatherFatherFather域,执行Decrease−KeyDecrease-KeyDecreaseKey的时候,把该节点xxx的子树抽出来(只是将FatherxFather_xFatherx设为000而不变动FatherxFather_xFatherx的结构)
让后和根节点合并,当Pop−MinPop-MinPopMin的时候,如果发现一个儿子的FatherFatherFather不是自己那么就不把他加入合并的队列中,这样不影响时间复杂度
时间复杂度,除了Pop−MinPop-MinPopMin都是O(1)O(1)O(1)的,Pop−MinPop-MinPopMin应该是均摊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]);
			}
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值