严格次小生成树

1555:【例 4】次小生成树

 这次是在图里面求严格次小生成树

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
const int maxn=1e5+10;
const int M=3e5+10;
const int INF=0x3fffffff;
typedef long long LL;
typedef unsigned long long ull;
/*
https://blog.csdn.net/regina8023/article/details/44814899
https://blog.csdn.net/YYHS_WSF/article/details/82842355
求出最小生成树,枚举每一条非树边,用它取代掉环上最大的边最优。
那么相当于求两点到lca的最大边权,直接倍增做。
但是要注意是严格最小生成树,那么这条非树边与环上最大边相同时可能取代掉的是环上的次大边,那么倍增的时候多维护一个严格次大的边即可。
*/
int n,m;
struct node{
    int u,v,wei;
}ed[M];
queue<int> q;
struct edge{
    int to,next,w;
}e[M*2];
struct ma{
    int a,b;  //分别是 最大、次大
}g[maxn][21],p;
int head[maxn],dep[maxn],f[maxn][21],fa[maxn];  //后两个:深度,倍增数组
int vis[M];
bool cmp(node a,node b){
    return a.wei<b.wei;
}
 
int findd(int x){
    if(x==fa[x]) return x;
    else return fa[x]=findd(fa[x]);
}
LL mst=0;
void  kru(){
    sort(ed+1,ed+1+m,cmp);
    int cnt=1;
    for(int i=1;i<=m;i++){
        int fa1=findd(ed[i].u);
        int fa2=findd(ed[i].v);
        if(fa1!=fa2){
            cnt++;
            vis[i]=1;
            mst+=ed[i].wei;
            fa[fa1]=fa2;
        }
    }
}
int tot;
void adde(int x,int y,int z){
    e[++tot].to=y;
    e[tot].next=head[x];
    e[tot].w=z;
    head[x]=tot;
    e[++tot].to=x;
    e[tot].next=head[y];
    e[tot].w=z;
    head[y]=tot;
}
 
void build(){
    for(int i=1;i<=m;i++){
        if(vis[i]) adde(ed[i].u,ed[i].v,ed[i].wei);  //建立树边
    }
    g[1][0].a=g[1][1].b=-INF; //最小值
    f[1][0]=0;  //根的爸爸
    dep[1]=1;
    q.push(1);
    while(!q.empty()){
        int x=q.front();
        q.pop();
        for(int i=head[x];i;i=e[i].next){
            int y=e[i].to;
            if(y==f[x][0]) continue;  //是爸爸
            f[y][0]=x;
            dep[y]=dep[x]+1;
            g[y][0].a=e[i].w;  //最大值的初值
            q.push(y);
        }
    }
}
 
void updat(ma &x,ma y){ ///用y来更新x
    if(x.a>y.a) x.b=max(y.a,x.b);   //b记录的是次大  ,这三行都是在更新次大
    else if(x.a<y.a) x.b=max(x.a,y.b);
    else x.b=max(x.b,y.b);
    x.a=max(x.a,y.a);  //最大直接更新
}
 
void ST(){
    for(int j=1;(1<<j)<=n;j++){
        for(int i=1;i<=n;i++){
            f[i][j]=f[f[i][j-1]][j-1];
            updat(g[i][j],g[i][j-1]);
            updat(g[i][j],g[f[i][j-1]][j-1]);
        }
    }
} 
void mov(int &x,int deep){  //先移动到同一层
    for(int i=20;i>=0;i--){
        if(dep[f[x][i]]>=deep) {
            updat(p,g[x][i]);
            x=f[x][i];
        }
    }
}
void getlca(int x,int y){
    p.a=p.b=-INF;
    if(dep[x]>dep[y]) swap(x,y);
    mov(y,dep[x]);  //先移动到同一层
    if(x==y) return;
    for(int i=20;i>=0;i--){
        if(f[x][i]!=f[y][i]){
            updat(p,g[x][i]);
            updat(p,g[y][i]);   //找到替换的那条边严格最大边
            x=f[x][i];
            y=f[y][i];
        }
    }
    updat(p,g[x][0]);
    updat(p,g[y][0]);
     
}
int main(){
    scanf("%d %d",&n,&m);
    for(int i=1;i<=m;i++){
        scanf("%d %d %d",&ed[i].u,&ed[i].v,&ed[i].wei);
    }
    for(int i=1;i<=n;i++) fa[i]=i;
    kru();  //先求出最小生成树的权值
    build();   //用非树边去更新倍增数组,g[][].a  g[][].b
    ST();
    LL ans=(LL)1e15;    //这个要设置的足够大
    for(int i=1;i<=m;i++){
        if(!vis[i]){   //用非树边建立
            getlca(ed[i].u,ed[i].v);
            if(p.a==ed[i].wei){  //最大便与其相等
                ans=min(ans,mst+ed[i].wei-p.b);
            }
            else ans=min(ans,mst+ed[i].wei-p.a);
        }
    }
    printf("%lld
",ans);
return 0;
}

  

原文地址:https://www.cnblogs.com/shirlybaby/p/13703198.html