P3366 【模板】最小生成树(boruvka/sollin)
boruvka/sollin
复杂度$O((m+n)logn)$
简要说明一下过程
引入一个数组$link[i]$表示连通块$i$下一步可更新的最短的边的编号
1.每次枚举所有边,如果边连接的2个点$(u,v)$不属于同连通块,那么更新$link[find(u)],link[find(v)]$(find(u)表示$u$所属的连通块)
2.枚举所有连通块,将$link[i]$两边的连通块合并。
3.如果第2步中有合并操作,则跳到1
注意更新$link[i]$,当比较的两条边权值相同时,要有确定的大小顺序,通常优先取编号小的。
#include<cstdio> #include<cstring> #define N 5005 #define M 200005 int n,m,k,ans,fa[N],link[N],U[M],V[M],W[M]; bool vis[M]; int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);} inline bool fc(int x,int y){return W[x]==W[y]?x<y:W[x]<W[y];} void boruvka(){ for(bool ok=1;ok;){ memset(link,0,sizeof(link)); ok=0; for(int i=1;i<=m;++i) if(!vis[i]){ int r1=find(U[i]),r2=find(V[i]); if(r1==r2) continue; if(fc(i,link[r1])) link[r1]=i; if(fc(i,link[r2])) link[r2]=i; } for(int i=1;i<=n;++i) if(!vis[link[i]]&&link[i]){ vis[link[i]]=ok=1; ans+=W[link[i]]; ++k; fa[find(U[link[i]])]=find(V[link[i]]); } } } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;++i) fa[i]=i; for(int i=1;i<=m;++i) scanf("%d%d%d",&U[i],&V[i],&W[i]); W[0]=1e9; boruvka(); k==n-1 ? printf("%d",ans):puts("orz"); return 0; }