浅谈次小生成树

今天跟大家聊聊怎么求次小生成树。
首先需要注意的是我们所说的次小生成树是指严格次小生成树。也就是我们要求的是权值大于最小生成树的权值最小的生成树。
我们求次小生成树是基于最小生成树的。我们求出最小生成树,那么显然次小生成树只会改变最小生成树上的一条边。
所以我们枚举每条非最小生成树上的边,那么我们只要从这条边的两点在树上的树链里求出权值最大的一条边,然后加入这条边的生成树就是原来的值num加上这条边的值val,再减去链上最大值max。
但是我们要求的是严格的,所以得保证max< val,然而有时会出现max==val的情况。所以我们不仅要记录树链的最大值,还要记录树链上的次大值(当然得小与最大值)。
问题在于怎么维护,维护的方法有多种多样,这里我选择了倍增+LCA的方法,就是用倍增来求并记录值。当然也可以用树剖写。
维护次大值确实很烦,导致代码又臭又长。


//Ma记录最大值,Tw记录次大值,gran就是祖先
#include<bits/stdc++.h>
#define MAX 100005
#define MAXN 600005
#define ll long long
using namespace std;
ll read(){
    char c;ll x;while(c=getchar(),c<'0'||c>'9');x=c-'0';
    while(c=getchar(),c>='0'&&c<='9') x=x*10+c-'0';return x;
}
ll n,m,k,cnt,ans,tot,fa[MAX],head[MAXN],nxt[MAXN],Ma[MAXN][10],Tw[MAXN][10],dep[MAXN],gran[MAXN][10];
struct edge{
    ll to,val;
}L[MAXN];
struct node{
    ll a,b,len,vis;
}F[MAXN];
ll cmp(node a,node b){
    return a.len<b.len;
}
ll find(ll x){
    if(fa[x]!=x) fa[x]=find(fa[x]);
    return fa[x];
}
void add(ll x,ll y,ll c){
    L[cnt]=(edge){y,c};
    nxt[cnt]=head[x];head[x]=cnt;cnt++;
    L[cnt]=(edge){x,c};
    nxt[cnt]=head[y];head[y]=cnt;cnt++;
}
void dfs(ll x,ll fa){
    for(ll i=head[x];i!=-1;i=nxt[i]){
        ll to=L[i].to;
        if(dep[to]) Ma[x][0]=L[i].val;
    }
    dep[x]=dep[fa]+1;gran[x][0]=fa;
    for(ll i=1;(1<<i)<=dep[x];i++){
        gran[x][i]=gran[gran[x][i-1]][i-1];
        if(Ma[x][i-1]<Ma[gran[x][i-1]][i-1]){
            Ma[x][i]=Ma[gran[x][i-1]][i-1];
            Tw[x][i]=max(Ma[x][i-1],Tw[gran[x][i-1]][i-1]);
        }
        else{
            Ma[x][i]=Ma[x][i-1];
            if(Ma[gran[x][i-1]][i-1]<Ma[x][i-1]) Tw[x][i]=max(Tw[x][i-1],Ma[gran[x][i-1]][i-1]);
            else Tw[x][i]=max(Tw[x][i-1],Tw[gran[x][i-1]][i-1]);
        }
    }
    for(ll i=head[x];i!=-1;i=nxt[i]){
        ll to=L[i].to;
        if(!dep[to]) dfs(to,x);
    }
}
pair<ll,ll> LCA(ll x,ll y){
    ll m1=0,m2=0;
    if(dep[x]>dep[y]) swap(x,y);
    for(ll i=9;i>=0;i--)
     if(dep[gran[y][i]]>=dep[x]){
        if(Ma[y][i]>m1) m2=m1,m1=Ma[y][i];
        else if(Ma[y][i]<m1&&Ma[y][i]>m2) m2=Ma[y][i];
        if(Tw[y][i]<m1&&Tw[y][i]>m2) m2=Tw[y][i];
        y=gran[y][i];
     }
    for(ll i=9;i>=0;i--)
     if(gran[x][i]!=gran[y][i]){
        if(Ma[x][i]>Ma[y][i]){
            if(Ma[x][i]>m1) m2=m1,m1=Ma[x][i];
            if(Ma[x][i]<m1&&Ma[x][i]>m2) m2=Ma[x][i];
            if(max(Tw[x][i],Ma[y][i])>m2){
                m2=max(Tw[x][i],Ma[y][i]);
             }
         }
        else{
            if(Ma[y][i]>m1) m2=m1,m1=Ma[y][i];
            if(Ma[x][i]<m1&&Ma[x][i]>m2) m2=Ma[x][i];
            if(max(Ma[x][i],Tw[y][i])>m2){
                if(max(Ma[x][i],Tw[y][i])<m1) m2=max(Ma[x][i],Tw[y][i]);
                else m2=max(Tw[x][i],Tw[y][i]);
             }          
        }
        x=gran[x][i];y=gran[y][i];
     }
    if(x!=y){
        if(Ma[y][0]>m1) m2=m1,m1=Ma[y][0];
        else if(Ma[y][0]<m1&&Ma[y][0]>m2) m2=Ma[y][0];
        if(Tw[y][0]>m2) m2=Tw[y][0];
        if(Ma[x][0]>m1) m2=m1,m1=Ma[x][0];
        else if(Ma[x][0]<m1&&Ma[x][0]>m2) m2=Ma[x][0];
        if(Tw[x][0]>m2) m2=Tw[x][0];
    }
    return make_pair(m1,m2);
}
int main()
{
    n=read();m=read();ans=1e18;
    memset(head,-1,sizeof(head));
    for(ll i=1;i<=m;i++){
        ll a=read(),b=read(),c=read();
        F[i]=(node){a,b,c};
    }
    sort(F+1,F+1+m,cmp);
    for(ll i=1;i<=n;i++) fa[i]=i;
    for(ll i=1;i<=m;i++){
        if(k==n-1) break;
        ll fx=find(F[i].a),fy=find(F[i].b);
        if(fx!=fy){
            fa[fy]=fx;k++;F[i].vis=1;tot+=F[i].len;
            add(F[i].a,F[i].b,F[i].len);
        }
    }
    dfs(1,0);
    for(ll i=1;i<=m;i++){
        if(F[i].vis) continue;
        pair<ll,ll> e=LCA(F[i].a,F[i].b);
        if(e.first<F[i].len) ans=min(ans,tot+F[i].len-e.first);
        else ans=min(ans,tot+F[i].len-e.second);
    }
    printf("%lld",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值