BZOJ1977 严格次小生成树

本文深入探讨了次小生成树的求解方法,通过在最小生成树的基础上进行边的替换,结合最大边和次大边的预处理,以及使用LCA算法优化路径查找,提供了一种高效求解次小生成树的方案。

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

最小生成树,LCA

次小生成树必定是从最小生成树中删掉一条边,再加入一条不会成环的边即可。

预处理出每个点到其祖先的路径上的最大边以及次大边。

那么我们可以枚举每条非树边,如果这条边比u,v路上的所有边都大的话,则可以替换掉最大的边作为次小生成树的备选。

如果该边与最大边相等,那么可以用该边替换掉路径中的次大边,作为备选答案。

 

预处理最大边及次大边的话我们需要在bfs处理节点上翻中加入一些东西。来,看书 《算法竞赛进阶指南》--李煜东著

 

 

 

 

 

 

 


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

typedef long long ll;

const int maxn=1e5+7;
const int maxm=3e5+7;
const int inf=0x3f3f3f3f;

struct Edge{
    int v,w,next;
}edge[maxn<<1];
int head[maxn],top;

void add(int u,int v,int w){
    edge[top].v=v;
    edge[top].w=w;
    edge[top].next=head[u];
    head[u]=top++;
}

int fre[maxn];

struct Node{
    int u,v,w;
    bool f;
    bool operator <(const Node& x)const{ return w<x.w; }
}a[maxm];


void init(){
    memset(head,-1,sizeof(head));
    top=0;
    for(int i=0;i<maxn;++i) fre[i]=i;
}

int myfind(int x){
    int r=x;
    while(r!=fre[r]) r=fre[r];
    int i=x,j;
    while(i!=r){
        j=fre[i];
        fre[i]=r;
        i=j;
    }
    return r;
}

bool liantong(int x,int y){
    int fx=myfind(x);
    int fy=myfind(y);
    if(fx!=fy){
        fre[fx]=fy;
        return 1;
    }
    return 0;
}
int mi=18;

int d[maxn],dis1[maxn][22],dis2[maxn][22],fa[maxn][22];

queue<int> q;
void bfs(int st){
    while(!q.empty()) q.pop();
    d[st]=1;
    dis1[st][0]=0;
    dis2[st][0]=-1;
    q.push(st);
    int u,v,w;
    while(!q.empty()){
        u=q.front(); q.pop();
        for(int i=head[u];i!=-1;i=edge[i].next){
            v=edge[i].v;
            w=edge[i].w;
            if(d[v]) continue;
            d[v]=d[u]+1;
            dis1[v][0]=w;
            dis2[v][0]=-1;

            fa[v][0]=u;
            for(int j=1;j<=mi;++j){
                fa[v][j]=fa[fa[v][j-1]][j-1];
                dis1[v][j]=max(dis1[v][j-1],dis1[fa[v][j-1]][j-1]);
                if(dis1[v][j-1]==dis1[fa[v][j-1]][j-1])
                    dis2[v][j]=max(dis2[v][j-1],dis2[fa[v][j-1]][j-1]);
                else if(dis1[v][j-1]<dis1[fa[v][j-1]][j-1])
                    dis2[v][j]=max(dis1[v][j-1],dis2[fa[v][j-1]][j-1]);
                else
                    dis2[v][j]=max(dis2[v][j-1],dis1[fa[v][j-1]][j-1]);
            }

            q.push(v);
        }
    }
}

pair<int,int> LCA(int x,int y){
    if(d[x]>d[y]) swap(x,y);
    int v1=-1,v2=-1;
    for(int i=mi;i>=0;--i)
        if(d[fa[y][i]]>=d[x]){
            if(v1>dis1[y][i]){
                v2=max(v2,dis1[y][i]);
            }
            else{
                v1=dis1[y][i];
                v2=max(v2,dis2[y][i]);
            }
            y=fa[y][i];
        }
    if(x==y) return make_pair(v1,v2);
    for(int i=mi;i>=0;--i){
        if(fa[y][i]!=fa[x][i]){
            if(v1>dis1[x][i]) v2=max(v2,dis1[x][i]);
            else v1=dis1[x][i],v2=max(v2,dis2[x][i]);
            if(v1>dis1[y][i]) v2=max(v2,dis1[y][i]);
            else v1=dis1[y][i],v2=max(v2,dis2[y][i]);

            x=fa[x][i];
            y=fa[y][i];
        }
    }
    if(v1>dis1[x][0]) v2=max(v2,dis1[x][0]);
    else v1=dis1[x][0],v2=max(v2,dis2[x][0]);
    if(v1>dis1[y][0]) v2=max(v2,dis1[y][0]);
    else v1=dis1[y][0],v2=max(v2,dis2[y][0]);

    return make_pair(v1,v2);
}
pair<int,int> p;
int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=0;i<m;++i){
        scanf("%d%d%d",&a[i].u,&a[i].v,&a[i].w);
    }
    int f=0;
    init();
    sort(a,a+m);
    ll sum=0;
    for(int i=0;i<m;++i){
        if(liantong(a[i].u,a[i].v)){
            add(a[i].u,a[i].v,a[i].w);
            a[i].f=1;
            add(a[i].v,a[i].u,a[i].w);
            sum+=a[i].w;
            ++f;
            if(f==n-1) break;
        }
    }

    bfs(1);
    ll res=0x3f3f3f3f3f3f3f3f;
    for(int i=0;i<m;++i)
        if(a[i].f==0){
            p=LCA(a[i].u,a[i].v);
            int v1=p.first,v2=p.second;
            if(a[i].w>v1) res=min(res,sum-v1+a[i].w);
            else if(a[i].w==v1) res=min(res,sum-v2+a[i].w);
        }
    printf("%lld\n",res);
    return 0;

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值