欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

严格次小生成树

程序员文章站 2022-04-11 10:24:34
...

链接: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();
}
相关标签: LCA