严格次小生成树

链接:https://ac.nowcoder.com/acm/contest/1057/G
在这里插入图片描述
复制

5 6 
1 2 1 
1 3 2 
2 4 3 
3 5 4 
3 4 3 
4 5 6

输出
11

思路
严格次小生成树与最小生成树只有一条边不同

#include <stdio.h>
#include <algorithm>
#include <queue>
#include <math.h>
#define INF 0x3f3f3f3f
#define ll long long
using namespace std;
int n,m,tot,t;
ll tem;
int head[100005],nex[200005],ver[200005],d[200005];
int dep[100005],maxi[100005][20],mini[100005][20],fa[100005][20],f[100005];//maxi存储最大值的边,mini存储次大值的边
struct node                                      //存原图
{
    int u,v,d;
    bool vis;
    bool operator<(const node &a)const{return d<a.d;};
}e[300005];
void add(int u,int v,int z)           //存最小生成树
{
    nex[++tot]=head[u];
    ver[tot]=v;
    d[tot]=z;
    head[u]=tot;
}
int find(int x)
{
    return x==f[x]?x:f[x]=find(f[x]);
}
void kruskal()
{
    for(int i=1;i<=n;i++)f[i]=i;
    sort(e+1,e+m+1);
    for(int i=1;i<=m;i++)
    {
        int x=find(e[i].u),y=find(e[i].v);
        if(x==y)continue;
        f[x]=y;
        e[i].vis=1;
        add(e[i].u,e[i].v,e[i].d);
        add(e[i].v,e[i].u,e[i].d);
        tem+=e[i].d;
    }
}
void bfs()
{
	queue<int>q;
    q.push(1);
    dep[1]=1;
    while(!q.empty())
    {
        int u=q.front();q.pop();
        for(int i=head[u];i;i=nex[i])
        {
            int v=ver[i];
            if(dep[v])continue;
            dep[v]=dep[u]+1;
            fa[v][0]=u;
            maxi[v][0]=d[i];                                      //记得赋值
            mini[v][0]=-INF;
            for(int j=1;j<=t;j++)                              //来更新最大值和次大值
            {
                fa[v][j]=fa[fa[v][j-1]][j-1];
                maxi[v][j]=max(maxi[v][j-1],maxi[fa[v][j-1]][j-1]);
                if(maxi[v][j-1]>maxi[fa[v][j-1]][j-1])
                    mini[v][j]=max(maxi[fa[v][j-1]][j-1],mini[v][j-1]);
                else if(maxi[v][j-1]==maxi[fa[v][j-1]][j-1])
                    mini[v][j]=max(mini[v][j-1],mini[fa[v][j-1]][j-1]); //次大不等于最大(严格限制)
                else                                                    //因为若最大和当前更新权一样时,
                    mini[v][j]=max(maxi[v][j-1],mini[fa[v][j-1]][j-1]); //我们要用次大和当前权跟新,但如果次大等于最大,则错误 
        	}
        	q.push(v);
        }
    }
}
int LCA(int x,int y) //求出公共祖先,为什么不想bfs一样在bfs中把 最大值和次大值求出来呢           
{                    //因为 我们在计算时要顾及两个最大,次大 这样很麻烦
    if(dep[x]>dep[y])swap(x,y);
    for(int i=t;i>=0;i--)
		if(dep[fa[y][i]]>=dep[x])y=fa[y][i];
    if(x==y) return x;
    for(int i=t;i>=0;i--)
        if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
    return fa[x][0];                
}
int get_max(int x,int y,int mx) //x为一个点,y为它的祖先 求出x到y 不等于当前权的最大值
{
	int ans=-INF;
	for(int i=t;i>=0;i--)
	{
		if(dep[fa[x][i]]>=dep[y])
		{
			if(maxi[x][i]!=mx)ans=max(ans,maxi[x][i]);   //最大边如果等于当前权,则用次大值更新
			else ans=max(ans,mini[x][i]);
			x=fa[x][i];
		}
	}
	return ans;
}
void solve()
{
	ll ans=2147483647000000;
	for(int i=1;i<=m;i++)
	{
		if(!e[i].vis)
		{
			int u=e[i].u,v=e[i].v,dis=e[i].d;
			int lca=LCA(u,v);
			int now1=get_max(u,lca,dis);  //分别求出两条边到祖先的不等于当前权的最大值
			int now2=get_max(v,lca,dis);
			ans=min(ans,tem-max(now1,now2)+dis);
		}
	}
	printf("%lld\n",ans);
}
int main()
{
    scanf("%d%d",&n,&m);
    t=(int)(log(n)/log(2))+1;
    for(int i=1;i<=m;i++)
        scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].d);
    kruskal();
    bfs();
    solve();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值