Boruvka 最小生成树

思想及流程

Boruvka 是 Kruskal 和 Prim 两者的结合算法,“博采众长”因此能轻松解决一类两者很难快速解决的问题,尤其是完全图(稠密图)的 MST。

Boruvka 的思想是:最开始每个点都是一个孤立的连通块,之后经过多轮的迭代,向 MST 边集中加边,把边的两端的连通块合并。加边的方法是:对每个连通块找到离它最近的连通块,这条边叫做这个连通块的“最小边”,每一轮结束时将所有拥有最小边的连通块的最小边加入 MST 边集;这一步可以用 \(O(E)\) 地枚举边并更新两端连通块的最小边来做到,但很多时候也会采用 dp 等方法直接求得这个离它最近的连通块。每一轮结束后,所有连通块都设为“没有最小边”,并进入下一轮。对于一个连通图来说,合并一轮下来,都会将连通块的数量至少减半,极端情况是 \(V/2\) 个连通块和另外 \(V/2\) 个连通块刚好分别是 \(V/2\) 个最小边的两端。这样一来,最多只会迭代 \(\log V\) 轮,常见时间复杂度为 \(O(E\log V)\)。(当然主要取决于找最小边的方法)

推荐题目

CODE-FESTIVAL-2017-FINAL Tree MST

这个题就是典型的使用了 dp 的方法来寻找连通块的最近连通块。在每轮中,我们可以用树形 dp 维护一个点的子树里离它最近的两个颜色互异的点(最近的含义是“完全图”中边权最小)。这样一来,这两个点中至少一个的颜色和 \(x\) 不一样,而这样的信息也容易为父亲所用。再进行一道换根 dp,得到离每个点最近的、跟这个点颜色不一样的点,用“点的最小边”更新点所在连通块的最小边,照搬 Boruvka 的流程即可。

/*
Todo list:
1. How to embed 'whether a node has miniedge' into ppp
2. How to connect in BCJ
3. You haven't changed vertex to color! DONE
*/
#include <bits/stdc++.h>
#define int long long
#define pii pair<int,int>
#define ppp pair<pii,pii >
#define fi first
#define se second
#define mkp make_pair
using namespace std;
const int N=2e5+5,INF=1e17;
const ppp nu=mkp(mkp(INF,0),mkp(INF,0));
int n,n_v,n_e,ans,w[N],fa[N];
bool bk[N];
vector<pii>gr[N],G[N];
vector<ppp>f[N]; // nearest 2 colors in x's subtree
pair<pii,int>mn[N]; // to store the miniedges for each routine
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
void unite(int x,int y){fa[find(y)]=find(x);}
inline void adde(int u,int v,int w){
	gr[u].push_back(mkp(v,w)),gr[v].push_back(mkp(u,w));
}
void dfs0(int x,int p){
	for(int i=0;i<gr[x].size();i++){
		int y=gr[x][i].first,z=gr[x][i].second;
		if(y^p)G[x].push_back(mkp(y,z)),dfs0(y,x);
	}
}
inline pair<pii,int>gm(pair<pii,int>a,pair<pii,int>b){
	return a.se<b.se?a:b;
}
inline ppp merge(ppp a,ppp b){
	vector<pii>tmp;
	tmp.push_back(a.fi),tmp.push_back(a.se),tmp.push_back(b.fi),tmp.push_back(b.se);
	sort(tmp.begin(),tmp.end(),[](pii a,pii b){return a.fi<b.fi;});
	ppp c;
	c.fi=tmp[0];
	if(tmp[0].se!=tmp[1].se)c.se=tmp[1];
	else c.se=tmp[2];
	return c;
}
void de(ppp a){
	printf("(%lld %lld, %lld %lld)\n",a.fi.fi,a.fi.se,a.se.fi,a.se.se);
}
void dfs1(int x){
	vector<ppp>pre,suf;
	f[x].resize(G[x].size()+1),pre.resize(G[x].size()),suf.resize(G[x].size());
	for(int i=0;i<G[x].size();i++){
		int y=G[x][i].first,z=G[x][i].second;
		dfs1(y);
		ppp tmp=f[y][G[y].size()],tmp2=mkp(mkp(w[y],find(y)),mkp(INF,0));
		tmp=merge(tmp,tmp2);
		tmp.fi.fi+=z,tmp.se.fi+=z; 
		pre[i]=(!i)?tmp:merge(pre[i-1],tmp);
	}
	for(int i=(int)G[x].size()-1;~i;i--){
		int y=G[x][i].first,z=G[x][i].second;
		ppp tmp=f[y][G[y].size()],tmp2=mkp(mkp(w[y],find(y)),mkp(INF,0));
		tmp=merge(tmp,tmp2);
		tmp.fi.fi+=z,tmp.se.fi+=z; 
		suf[i]=i==G[x].size()-1?tmp:merge(suf[i+1],tmp);
	}
	for(int i=0;i<G[x].size();i++){
		f[x][i]=merge((!i)?nu:pre[i-1],i==G[x].size()-1?nu:suf[i+1]);
	}
	f[x][G[x].size()]=G[x].size()?suf[0]:nu;
	
}
void dfs2(int x,int p,int pe,int pid){
	if(p){
		ppp tmp=f[p][pid],tmp2=mkp(mkp(w[p],find(p)),mkp(INF,0));
		tmp=merge(tmp,tmp2);
		tmp.fi.fi+=pe,tmp.se.fi+=pe;
		for(int i=0;i<=G[x].size();i++){
			f[x][i]=merge(f[x][i],tmp);
		}
	}
//	printf("%lld: ",x),de(f[x][G[x].size()]);
	if(f[x][G[x].size()].fi.fi<INF&&f[x][G[x].size()].fi.se!=find(x))mn[find(x)]=gm(mn[find(x)],mkp(mkp(find(x),f[x][G[x].size()].fi.se),f[x][G[x].size()].fi.fi+w[x]));
	else if(f[x][G[x].size()].se.fi<INF&&f[x][G[x].size()].se.se!=find(x))mn[find(x)]=gm(mn[find(x)],mkp(mkp(find(x),f[x][G[x].size()].se.se),f[x][G[x].size()].se.fi+w[x]));
	for(int i=0;i<G[x].size();i++){
		int y=G[x][i].first,z=G[x][i].second;
		dfs2(y,x,z,i);
	}
}
void Boruvka(){
	n_v=n_e=0;
	for(int i=0;i<=n;i++)f[i].clear(),mn[i]=mkp(mkp(0,0),INF);
	dfs1(1),dfs2(1,0,0,0);
	for(int i=1;i<=n;i++)if(find(mn[i].fi.fi)!=find(mn[i].fi.se))unite(mn[i].fi.fi,mn[i].fi.se),ans+=mn[i].se/*,printf("%lld %lld %lld\n",e[i].fi.fi,e[i].fi.se,e[i].se)*/;
//	puts("......");
	for(int i=1;i<=n;i++)bk[find(i)]=1;
	for(int i=1;i<=n;i++)n_v+=bk[i],bk[i]=0;
}
inline int read(){
	int x=0;char ch=getchar();
	while(ch<'0'||ch>'9')ch=getchar();
	while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	return x;
}
signed main(){
//	freopen("input.in","r",stdin);freopen("output.out","w",stdout);
	n=read();
	for(int i=1;i<=n;i++)w[i]=read(),fa[i]=i;
	for(int i=1,u,v,_w;i<n;i++){
		u=read(),v=read(),_w=read();
		adde(u,v,_w);
	}
	dfs0(1,0);
	n_v=n;
	for(int i=1;n_v>1;i++)Boruvka();
	cout<<ans;
}
posted @ 2022-04-10 17:39  pengyule  阅读(470)  评论(0)    收藏  举报