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

P3366 【模板】最小生成树

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;
}

 

posted @ 2019-03-15 20:28  kafuuchino  阅读(296)  评论(0编辑  收藏  举报