bzoj 1977严格次小生成树

本文深入探讨了求解严格次小生成树的算法实现,通过Kruskal算法构建最小生成树,并使用DFS和LCA算法寻找次优解。文章详细介绍了算法流程,包括边的排序、并查集应用、祖先查找、最大值和次大值更新,以及LCA计算,最终输出次小生成树的总权重。

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

原文链接:https://blog.youkuaiyun.com/BerryKanry/article/details/77983140

题目大意:求严格次小生成树 

#include<stdio.h>
#include<algorithm>
#include<cstring>
#include<iostream>
using namespace std;
typedef long long dnt;

const int INF=0x3f3f3f3f;

struct edge
{
    int u,v,w,last;
}ed[2000010],sid[2000010];

int n,m,num=0,tot=0,ans=INF;
int head[100010],fa[100010],dep[100010];
int anc[100010][20],st[100010][20],sd[100010][20],used[300010];
dnt bns=0;

bool cmp(const edge &a,const edge &b)
{
    return a.w<b.w;
}

void add(int u,int v,int w)
{
    num++;
    ed[num].v=v;
    ed[num].w=w;
    ed[num].last=head[u];
    head[u]=num;
}

int getfather(int x)
{
    if(x==fa[x]) return x;
    return fa[x]=getfather(fa[x]);
}

void Kruscal()
{
    sort(sid+1,sid+m+1,cmp);
    for(int i=1;i<=n;i++)
        fa[i]=i;
    for(int i=1;i<=m;i++)
    {
        int x=getfather(sid[i].u),y=getfather(sid[i].v);
        if(x!=y)
        {
            used[i]=1;
            tot++,bns+=sid[i].w;
            add(sid[i].u,sid[i].v,sid[i].w);
            add(sid[i].v,sid[i].u,sid[i].w);
            fa[x]=y;
        }
        if(tot==n-1) return ;
    }
    return ;
}

void dfs(int u,int f)
{///  这个代码的精华所在
    anc[u][0]=f;
    for(int p=1;p<=16;p++)
    {
        anc[u][p]=anc[anc[u][p-1]][p-1],/// 祖先部分
        st[u][p]=max(st[u][p-1],st[anc[u][p-1]][p-1]);///  最大值部分(显然  ST表)
        if(st[u][p-1]==st[anc[u][p-1]][p-1])
            sd[u][p]=max(sd[u][p-1],sd[anc[u][p-1]][p-1]);/// 到祖先的两部分如果最大值都相等的话 那么直接从两部分的次大值转移过来一定没错
        else
        {
            sd[u][p]=min(st[u][p-1],st[anc[u][p-1]][p-1]);///要么是两个最大值的较小的
            sd[u][p]=max({sd[u][p],sd[u][p-1],sd[anc[u][p-1]][p-1]});///还要考虑是否有一部分的次小时大于上面的那个较小的最大值
        }
    }
    for(int i=head[u];i;i=ed[i].last)
    {
        int v=ed[i].v;
        if(v==f) continue ;
        dep[v]=dep[u]+1;
        st[v][0]=ed[i].w;
        dfs(v,u);
    }
}

void calmx(int wth,int vth,int &d,int &e)
{
    if(wth==d) return ;
    if(wth>d) e=d,d=wth;
    else if(wth>e) e=wth;
    e=max(e,vth);
}

int lca(int u,int v,int xx)
{
    int d=0,e=0;
    if(dep[u]<dep[v]) swap(u,v);
    for(int i=16;i>=0;i--)
        if(dep[anc[u][i]]>=dep[v])
        {
            calmx(st[u][i],sd[u][i],d,e);
            u=anc[u][i];
        }
    if(u==v)
    {
        if(xx==d) return e;
        return d;
    }
    for(int i=16;i>=0;i--)
        if(anc[u][i]!=anc[v][i])
        {
            calmx(st[u][i],sd[u][i],d,e);calmx(st[v][i],sd[u][i],d,e);
            u=anc[u][i],v=anc[v][i];
        }
    calmx(st[u][0],sd[u][0],d,e),calmx(st[v][0],sd[v][0],d,e);
    if(xx==d) return e;
    return d;
}

int main()
{
//    freopen("input2.txt","r",stdin);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
        scanf("%d%d%d",&sid[i].u,&sid[i].v,&sid[i].w);
    Kruscal();
    dfs(1,1);
    for(int i=1;i<=m;i++)
    {
        if(used[i]) continue ;
        int u=sid[i].u,v=sid[i].v;
        int tmp=lca(u,v,sid[i].w);
        ans=min(ans,sid[i].w-tmp);
    }
    cout << ans+bns <<endl ;
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值