配对堆学习笔记

配对堆学习笔记

可并堆,比较好写,不能持久化。

结构是一个儿子指针一个兄弟指针,根链式前向星差不多。合并就直接把大的堆作为小的堆的儿子,删除就两两配对暴力合并儿子,这样好像可以让之后儿子数量少一点。具体的要用势能分析我也不会/kk

例题:P3377

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int read(){
	int x=0,pos=1;char ch=getchar();
	for(;!isdigit(ch);ch=getchar()) if(ch=='-') pos=0;
	for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
	return pos?x:-x;
}
#define ll long long
#define FOR(i,a,b) for(int i=a;i<=b;i++)
const int N = 1e5+200;
int f[N];
int gf(int x){
	if(f[x]==x) return x;
	else return f[x]=gf(f[x]); 
}
int nex[N],son[N];int cnt;ll val[N];
void add(int u,int v){ //v作为u的儿子 
	nex[v]=son[u];son[u]=v;
} 
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);add(u,v);return u;
}
int merges(int u){//把u和u的兄弟两两配对,merge到一起 
	if(!u) return 0; if(!nex[u]) return u;
	int v1=nex[u],v2=nex[v1];
	nex[u]=nex[v1]=0;return merge(merge(u,v1),merges(v2));
} 
/*
void dfs(int u){
	ans[++top]=val[u];
	for(int i=son[u];i;i=nex[i]) dfs(i);
}*/
struct heap{
	int rt;
	void push(ll x){val[++cnt]=x;rt=merge(rt,cnt);}
	ll top(){return val[rt];}
	void pop(){rt=merges(son[rt]);}
	void join(int now){rt=merge(rt,now);} 
}h[N];
int main(){
	int n=read(),m=read();
	FOR(i,1,n){
		int x=read();f[i]=i;h[i].push(x);
	}
	FOR(i,1,m){
		int opt=read();
		if(opt==1){
			int x=read(),y=read();int fu=gf(x),fv=gf(y);
			if(val[x]==-1||val[y]==-1||fu==fv) continue;
			f[fv]=fu;
			h[fu].join(h[fv].rt); 
		}else{
			int x=read();
			if(val[x]==-1) printf("-1\n");
			else{
				int fu=gf(x);printf("%d\n",h[fu].top());val[h[fu].rt]=-1;h[fu].pop();
			}	
		}
	}
	return 0;
}

例题 洛谷P3642 [APIO2016]烟火表演

这题是维护凸包并,平移线段(插入拐点),弹出右端的一些线段

由于斜率有规律,我们直接用可并堆维护拐点即可。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int read(){
	int x=0,pos=1;char ch=getchar();
	for(;!isdigit(ch);ch=getchar()) if(ch=='-') pos=0;
	for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
	return pos?x:-x;
}
#define ll long long
#define FOR(i,a,b) for(int i=a;i<=b;i++)
#define ROF(i,a,b) for(int i=a;i>=b;--i)
const int N = 6e6+200;
int f[N];
int gf(int x){
	if(f[x]==x) return x;
	else return f[x]=gf(f[x]); 
}
int nex[N],son[N],d[N];int cnt;ll val[N],w[N];
void add(int u,int v){ //v作为u的儿子 
	nex[v]=son[u];son[u]=v;
} 
int merge(int u,int v){//大根堆 
	if(!u||!v) return u^v;
	if(val[u]<val[v]) swap(u,v);add(u,v);return u;
}
int merges(int u){//把u和u的兄弟两两配对,merge到一起 
	if(!u) return 0; if(!nex[u]) return u;
	int v1=nex[u],v2=nex[v1];
	nex[u]=nex[v1]=0;return merge(merge(u,v1),merges(v2));
} 
ll ans[N],top=0;
void dfs(int u){
	ans[++top]=val[u];
	for(int i=son[u];i;i=nex[i]) dfs(i);
}
struct heap{
	int rt;
	void push(ll x){val[++cnt]=x;rt=merge(rt,cnt);}
	ll top(){return val[rt];}
	void pop(){rt=merges(son[rt]);}
	void join(int now){rt=merge(rt,now);} 
}h[N];
int main(){
	int n=read(),m=read(); 
	ll s0=0;
	FOR(i,2,n+m){
		f[i]=read(),w[i]=read();d[f[i]]++;s0+=w[i];
	}
	ROF(i,n+m,2){
		ll l=0,r=0;
		while(d[i]){
			d[i]--;r=h[i].top();h[i].pop();
		}l=h[i].top();h[i].pop();
		h[i].push(l+w[i]);h[i].push(r+w[i]);
		h[f[i]].join(h[i].rt); 
	}
	dfs(h[1].rt);
	sort(ans+1,ans+top+1);top-=d[1];
	FOR(i,0,top-1){
		s0-=1ll*(top-i)*(ans[i+1]-ans[i]);
	}
	printf("%lld\n",s0);
	return 0;
}
posted @ 2021-01-12 15:54  lcyfrog  阅读(48)  评论(0编辑  收藏