p4180 次小生成树

传送门

分析

次小生成树的求法有两种,最大众的一种是通过倍增LCA找环中最大边求解,而这里我介绍一种神奇的O(nlogn) 做法:

    我们先建立最小生成树,因为我们用kruskal求解是边的大小已经按升序排列,所以相同情况下,先枚举的边一定更优,所以我们每一次暴力的找非树边所连两点的LCA,并在寻找过程中对经过的边染色同时将其加入并查集以防止其二次查询(为何只需查找一次之前已经说过),然后在最后,我们只需找出所染颜色所代表的边的权值减去被染色的边的权值的最小值即可。因为被染色的树边共有n-1条,所以此过程的复杂度是O(m),因此总复杂度即为快排复杂度O(mlogm)。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<stack>
#include<map>
#include<vector>
#include<set>
#include<ctime>
#include<cctype>
using namespace std;
long long fa[110000],tot,used[310000],f[110000],col[310000],dep[110000];
long long ari[110000],is[110000];
long long sum=1;
struct node{
    long long x,y,z;
}d[310000];
struct edge{
    long long from,to,nxt,w,id;
}e[610000];
long long head[610000];
void add(long long x,long long y,long long z,long long id){
    e[sum].to=y;
    e[sum].nxt=head[x];
    e[sum].w=z;
    e[sum].id=id;
    head[x]=sum++;
    e[sum].to=x;
    e[sum].nxt=head[y];
    e[sum].w=z;
    e[sum].id=id;
    head[y]=sum++;
    return;
}
bool cmp(const node &p,const node &q){
    return p.z<q.z;
}
long long sf(long long a){
    return fa[a]==a?a:fa[a]=sf(fa[a]);
}
void dfs(long long a,long long fat){
    long long i,j,k;
    for(i=head[a];i;i=e[i].nxt)
       if(e[i].to!=fat){
        dep[e[i].to]=dep[a]+1;
        f[e[i].to]=a;
        ari[e[i].to]=e[i].id;
        dfs(e[i].to,a);
    }
    return;
}
long long ff(long long a){
    return is[a]==a?a:is[a]=ff(is[a]);
}
void mer(long long u,long long v,long long c){
    u=ff(u),v=ff(v);
    while(u!=v){
        if(dep[u]<dep[v])swap(u,v);
        col[ari[u]]=c;
        is[u]=ff(f[u]);
        u=ff(u);
    }
    return;
}
int main(){
    //freopen("1.in","r",stdin);
    long long n,m,i,j,k;
    scanf("%lld%lld",&n,&m);
    for(i=1;i<=m;i++){
       scanf("%lld%lld%lld",&d[i].x,&d[i].y,&d[i].z);
    }
    //建最小生成树
    sort(d+1,d+m+1,cmp);
    long long cnt=0,p,q;
    for(i=1;i<=n;i++){
       fa[i]=i;
       f[i]=i;
       is[i]=i;
    }
    for(i=1;i<=m;i++){
        p=sf(d[i].x),q=sf(d[i].y);
        if(p!=q){
            cnt++;
            if(rand()%2)fa[p]=q;
              else fa[q]=p;        
            tot+=d[i].z;
            used[i]=1;
            add(d[i].x,d[i].y,d[i].z,i);
        }
        if(cnt==n-1)break;
    }
    //初始化,f表示父子关系,is用于新并查集
    dfs(1,0);
    for(i=1;i<=m;i++)
       if(!used[i]){
            mer(d[i].x,d[i].y,i);
       }
    //求答案
    long long ans=1000000007;
    for(i=1;i<=m;i++)
       if(used[i]){
            if(col[i]&&d[col[i]].z!=d[i].z)
              ans=min(ans,d[col[i]].z-d[i].z);
       }
    printf("%lld\n",ans+tot);
    return 0;
}

posted @ 2018-05-09 19:13  水题收割者  阅读(163)  评论(0编辑  收藏  举报