Boruvka
大概是这样的:一开始图中有\(n\)个连通块,每次操作我们选出各个连通块连出去的最短的边(如果有相同边权的边的话可以把序号作为第二关键字),然后把这些边加入最小生成树。
最坏的情况下每次操作都会让当前的连通块减半,因此Boruvka算法的复杂度为\(O(m\log n)\)。
主要的应用在于边数为\(O(n^2)\)级别但是有特殊性质(如曼哈顿距离、异或等)的情况,选各个连通块连出去的边的时候可以不枚举每条边而是直接找。
#include<bits/stdc++.h>
const int N=5007,M=200007;
struct edge{int u,v,w;}e[M];
int vis[M],a[N],fa[N];
int read(){int x;scanf("%d",&x);return x;}
int cmp(int i,int j){return !j? 1:(e[i].w<e[j].w||(e[i].w==e[j].w&&i<j));}
int Find(int x){return x==fa[x]? x:fa[x]=Find(fa[x]);}
int main()
{
int n=read(),m=read(),i,cnt=1,sum=0,f=1,u,v;
for(i=1;i<=m;++i) e[i]=(edge){read(),read(),read()};
for(i=1;i<=n;++i) fa[i]=i;
while(f)
{
f=0,memset(a,0,sizeof a);
for(i=1;i<=m;++i)
{
if(vis[i]||(u=Find(e[i].u))==(v=Find(e[i].v))) continue;
if(cmp(i,a[u])) a[u]=i;
if(cmp(i,a[v])) a[v]=i;
}
for(i=1;i<=n;++i)
if(a[i]&&!vis[a[i]])
f=1,++cnt,sum+=e[a[i]].w,vis[a[i]]=1,fa[Find(e[a[i]].u)]=Find(e[a[i]].v);
}
cnt==n? printf("%d",sum):puts("orz");
}