[洛谷] [最小生成树] [并查集判环] P4180 严格次小生成树

· · 个人记录

题意

求无向图 G严格次小生成树,输出其边权和。

思路

代码

#include<bits/stdc++.h>
using namespace std;

const int N=1e5+5,M=3e5+5;
const long long INF=1000000000000005;
int n,m,u[M],v[M],cnt,fa[N],tot,head[N],id[M];
long long w[M],ans,sum,lg[N],jump[N][25],dep[N],Max[N][25],Max2[N][25],z,d[6];
struct edge{
    int u,v,jl;
    long long w;
}a[2*M];
struct qxx{
    int v,nxt;
    long long w;
}e[2*M];

inline void insert(int u,int v,long long w,int jl){
    a[++cnt].u=u;
    a[cnt].v=v;
    a[cnt].w=w;
    a[cnt].jl=jl;
}
inline bool cmp(edge x,edge y){
    return x.w<y.w;
}
inline int find(int k){
    if(fa[k]==k){
        return k; 
    }
    return fa[k]=find(fa[k]);
}
inline void add(int u,int v,long long w){
    e[++tot].v=v;
    e[tot].nxt=head[u];
    e[tot].w=w;
    head[u]=tot;
}
inline void kruskal(){ 
    sort(a+1,a+1+cnt,cmp);
    for(int i=1; i<=cnt;i++){
        int fu,fv;
        long long w;
        fu=find(a[i].u);
        fv=find(a[i].v);
        w=a[i].w;
        if(fu^fv){
            fa[fu]=fv;
            id[a[i].jl]=1;
            add(a[i].u,a[i].v,w); 
            add(a[i].v,a[i].u,w); //用 kruskal 方便建边
            sum+=w;
        } 
    }
}
inline void dfs(int u,int fa){ 
    dep[u]=dep[fa]+1;
    for(int i=1; i<=lg[dep[u]];++i){ //倍增的预处理
        jump[u][i]=jump[jump[u][i-1]][i-1];
        if(Max[u][i-1]^Max[jump[u][i-1]][i-1]){
            Max[u][i]=max(Max[u][i-1],Max[jump[u][i-1]][i-1]);
            Max2[u][i]=min(Max[u][i-1],Max[jump[u][i-1]][i-1]);
        }
        else{
            Max[u][i]=Max[u][i-1];
            Max2[u][i]=max(Max2[u][i-1],Max2[jump[u][i-1]][i-1]);
        }
    }
    for(int i=head[u]; i;i=e[i].nxt){
        int v=e[i].v;
        if(v^fa){
            Max[v][0]=e[i].w;
            jump[v][0]=u;
            dfs(v,u);
        } 
    }
}
inline int LCA(int x,int y){
    if(dep[x]<dep[y]){
        swap(x,y);
    }
    for(int i=lg[dep[x]]; i>=0;i--){
        if(dep[jump[x][i]]>=dep[y]){
            x=jump[x][i];
        }
    }
    if(x==y){
        return x;
    }
    for(int i=lg[dep[x]]; i>=0;i--){
        if(jump[x][i]^jump[y][i]){
            x=jump[x][i];
            y=jump[y][i];
        }
    }
    return jump[x][0];
}
inline int getmax(int x,int y){ //路径上的最大边
    long long ma=-1; //不存在就为 -1
    for(int i=lg[dep[x]]; i>=0;i--){
        if(dep[jump[x][i]]>=dep[y]){
            ma=max(ma,Max[x][i]);
            x=jump[x][i];
        }
    }
    return ma;
}
inline int getmax2(int x,int y){
    long long m1=-1,m2=-1; //不存在就为 -1
    for(int i=lg[dep[x]]; i>=0;i--){ 
        if(dep[jump[x][i]]>=dep[y]){
            if(Max[x][i]>m1){   
                m2=m1;
                m1=Max[x][i];
            }
            else{
                if(Max[x][i]>m2){
                    m2=Max[x][i];
                }
            } 
            if(Max2[x][i]>m1){  
                m2=m1;
                m1=Max2[x][i];
            }
            else{
                if(Max2[x][i]>m2){
                    m2=Max2[x][i];
                }
            } 
            x=jump[x][i];
        }
    }
    return m2;
}
int main(){
    cin>>n>>m;
    for(int i=1; i<=n;i++){
        fa[i]=i;
    }
    for(int i=1; i<=m;++i){
        scanf("%d%d%lld",&u[i],&v[i],&w[i]);
        if(u[i]==v[i]){
            continue; //忽略重边
        }
        insert(u[i],v[i],w[i],i);
        insert(v[i],u[i],w[i],i);
    }
    kruskal();

    memset(Max,-1,sizeof Max);
    memset(Max2,-1,sizeof Max2); //如果不存在则为 -1
    for(int i=2; i<=n;i++){
        lg[i]=lg[i>>1]+1;
    }
    dfs(1,0); 
    ans=INF;
    for(int i=1; i<=m;i++){
        if(!id[i]){
            int x,y,k;
            x=u[i];
            y=v[i];
            k=LCA(x,y);
            d[1]=getmax(x,k);
            d[2]=getmax2(x,k);
            d[3]=getmax(y,k);
            d[4]=getmax2(y,k);
            sort(d+1,d+5);
            int qq=4;
            while(qq&&d[qq]==w[i]){ //注意不能相同,相同就向下继续找
                qq--;
            }
            z=d[qq];
            if(z^-1){
                ans=min(ans,sum-z+w[i]);
            }
        }
    }
    cout<<ans;
    return 0;
}