P3366 【模板】最小生成树(boruvka/sollin)

时间:2023-03-09 17:04:58
P3366 【模板】最小生成树(boruvka/sollin)

P3366 【模板】最小生成树

boruvka/sollin

复杂度$O(mlogn)$

简要说明一下过程

引入一个数组$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=;ok;){
memset(link,,sizeof(link)); ok=;
for(int i=;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=;i<=n;++i) if(!vis[link[i]]&&link[i]){
vis[link[i]]=ok=; ans+=W[link[i]]; ++k;
fa[find(U[link[i]])]=find(V[link[i]]);
}
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=;i<=n;++i) fa[i]=i;
for(int i=;i<=m;++i) scanf("%d%d%d",&U[i],&V[i],&W[i]);
W[]=1e9; boruvka();
k==n- ? printf("%d",ans):puts("orz");
return ;
}