次小生成树

本文介绍了一种求解严格次小生成树的问题,通过先求最小生成树再枚举非树边的方式找到次小生成树的边权和。具体实现包括使用Kruskal算法求最小生成树,并通过深度优先搜索和并查集维护次小值。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

次小生成树

题目描述

小 C 最近学了很多最小生成树的算法,Prim 算法、Kurskal 算法、消圈算法等等。 正当小 C 洋洋得意之时,小 P 又来泼小 C 冷水了。小 P 说,让小 C 求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说: 如果最小生成树选择的边集是 E_MEM ,严格次小生成树选择的边集是 E_SES ,那么需要满足:\sum _{e \in E_S} value(e) < \sum _{e \in E_M} value(e)eESvalue(e)<eEMvalue(e) 。

这下小 C 蒙了,他找到了你,希望你帮他解决这个问题。

输入格式

第一行包含两个整数 nn 和 mm ,表示无向图的点数与边数。

接下来 mm 行,每行3个数 x,y,zx,y,z 表示,点 xx 和点 yy 之间有一条边,边的权值为 zz 。

输出格式

包含一行,仅一个数,表示严格次小生成树的边权和。


solution

 

次小生成树与最小生成树一定只差一条边。
如果差两条,我们一定可以把一条换回去。
那么就先求出最小生成树,枚举非树边,找它在树上连成的环上的最小值替代即可。
由于要严格次小,所以还得维护次小值,分讨即可。
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#define maxn 500005
using namespace std;
int n,m,head[maxn],tot,fl[maxn];
int par[maxn],deep[maxn];
long long ans;
struct node{
    int u,v,w,nex;
}e[maxn*2],E[maxn];
struct no{
    int f,m1,m2;
}st[maxn][22];
bool cmp(node a,node b){
    return a.w<b.w;
}
int getf(int k){return par[k]==k?k:par[k]=getf(par[k]);}
void lj(int t1,int t2,int t3){
    e[++tot].v=t2;e[tot].w=t3;e[tot].nex=head[t1];head[t1]=tot;
}
void Kruskal(){
    sort(E+1,E+m+1,cmp);
    for(int i=1;i<=n;i++)par[i]=i;
    int sum=0;
    for(int i=1;i<=m;i++){
        int f1=getf(E[i].u),f2=getf(E[i].v);
        if(f1!=f2){
            fl[i]=1;
            lj(E[i].u,E[i].v,E[i].w);
            lj(E[i].v,E[i].u,E[i].w);
            ans+=E[i].w;
            par[f1]=f2;sum++;if(sum==n-1)break;
        }
    }
}
void dfs(int k,int fa){
    st[k][0].f=fa;deep[k]=deep[fa]+1;
    for(int i=head[k];i;i=e[i].nex){
        if(e[i].v!=fa){
            st[e[i].v][0].m1=e[i].w;
            dfs(e[i].v,k);
        }
    }
}
no merge(no A,no B){
    no C;C.f=B.f;
    C.m1=max(A.m1,B.m1);
    C.m2=max(A.m2,B.m2);
    if(A.m1<C.m1)C.m2=max(C.m2,A.m1);
    if(B.m1<C.m1)C.m2=max(C.m2,B.m1);
    return C;
}
no get(int v,int u){
    no A;A.m1=A.m2=0;
    if(deep[u]<deep[v])swap(u,v);
    for(int x=20;x>=0;x--){
        if(deep[st[u][x].f]>=deep[v]){
            A=merge(A,st[u][x]);
            u=st[u][x].f;
        }
    }
    for(int x=20;x>=0;x--){
        if(st[u][x].f!=st[v][x].f){
            A=merge(A,st[u][x]);A=merge(A,st[v][x]);
            u=st[u][x].f,v=st[v][x].f;
        }
    }
    if(u==v)return A;
    A=merge(A,st[u][0]);A=merge(A,st[v][0]);
    return A;
}
int main(){
    cin>>n>>m;
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&E[i].u,&E[i].v,&E[i].w);
    }
    Kruskal();
    dfs(1,0);
    for(int j=1;j<=20;j++)
    for(int i=1;i<=n;i++){
        st[i][j]=merge(st[i][j-1],st[st[i][j-1].f][j-1]);
    }
    //for(int i=1;i<=n;i++)printf("%d %d %d\n",st[i][1].f,st[i][1].m1,st[i][1].m2);puts("");
    int M=1e9;
    for(int i=1;i<=m;i++){
        if(fl[i])continue;
        int u=E[i].u,v=E[i].v;
        no tmp=get(u,v);
        if(tmp.m1<E[i].w)M=min(M,E[i].w-tmp.m1);
        else if(tmp.m1==E[i].w)M=min(M,E[i].w-tmp.m2);
    }
    cout<<ans+M<<endl;
    return 0;
}
View Code

 

posted @ 2019-04-02 23:16 liankewei123456 阅读( ...) 评论( ...) 编辑 收藏
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值