【图论】次小生成树

【题目描述】
给定一张 N 个点 M 条边的无向图,求无向图的严格次小生成树。

设最小生成树的边权之和为sum,严格次小生成树就是指边权之和大于 sum 的生成树中最小的一个。

【输入】
第一行包含两个整数 N 和 M,表示无向图的点数与边数;

接下来 M 行,每行三个数 x,y,z,表示点 x 和点y 之间有一条边,边的权值为 z

【输出】
包含一行,仅一个数,表示严格次小生成树的边权和。

【样例输入】
5 6
1 2 1
1 3 2
2 4 3
3 5 4
3 4 3
4 5 6
【样例输出】
11

思路

首先我们可以证明次小生成树一定在一颗最小生成树的邻集中(即只有一条边不同)。
所以我们可以先求出最小生成树,枚举每一条非树边,利用lca维护非树边两端点之间路径上的最大值和次大值,并尝试用当前非树边替换,最后打擂台比较,得到最小生成树邻集中边权和最小的一颗生成树,就是次小生成树。
代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<queue>
#define re register
using namespace std;
long long n,m,a,b,c;
struct node{
	long long u,v,w;
}e[1000001],E[1000001];
typedef pair<int,int>T;
bool t[1000001];
long long fa[100001];
long long f[100001];
long long nxp[1000001],cnt=0;
long long dep[100001];
long long ff[100001][20];
long long fir[100001][20];
long long sec[100001][20];
inline void add(long long u,long long v,long long w){e[++cnt].u=u;e[cnt].w=w;e[cnt].v=v;nxp[cnt]=f[u];f[u]=cnt;}
long long cmp(node a,node b){return a.w<b.w;}
long long find(long long x){return fa[x]==x?x:fa[x]=find(fa[x]);}
void dfs(long long u)
{
	for(long long re i=1;(1<<i)<=dep[u];i++)
	{
		ff[u][i]=ff[ff[u][i-1]][i-1];
		fir[u][i]=max(fir[u][i-1],fir[ff[u][i-1]][i-1]);
		if(fir[u][i-1]!=fir[ff[u][i-1]][i-1])
			sec[u][i]=min(fir[u][i-1],fir[ff[u][i-1]][i-1]);
		sec[u][i]=max(sec[u][i],sec[u][i-1]);
		sec[u][i]=max(sec[u][i],sec[ff[u][i-1]][i-1]);
	}
	for(long long re i=f[u];i;i=nxp[i])
	{
		long long v=e[i].v;
		if(!dep[v])
		{
			dep[v]=dep[u]+1;
			fir[v][0]=e[i].w;
			ff[v][0]=u;
			dfs(v);
		}
	}
}
inline T lca(long long a,long long b)
{
	long long fi=-0x7ffff,se=-0x7ffff;
	if(dep[a]<dep[b])swap(a,b);
	long long tt=dep[a]-dep[b];
	for(long long re i=0;(1<<i)<=tt;i++)
		if(tt&(1<<i))
		{
			if(fi!=fir[a][i])
				se=max(se,min(fi,fir[a][i]));
			se=max(se,sec[a][i]);
			fi=max(fi,fir[a][i]);
			a=ff[a][i];
		}
	if(a==b)return make_pair(fi,se);
	for(long long re i=18;i>=0;i--)
	{
		if(ff[a][i]!=ff[b][i])
		{
			if(fi!=fir[a][i])
				se=max(se,min(fi,fir[a][i]));
			se=max(se,sec[a][i]);
			fi=max(fi,fir[a][i]); 
			a=ff[a][i];
			if(fi!=fir[b][i])
				se=max(se,min(fi,fir[b][i]));
			se=max(se,sec[b][i]);
			fi=max(fi,fir[b][i]);
			b=ff[b][i];
		}
	}
	se=max(se,sec[b][0]);
	se=max(se,sec[a][0]);
	if(fi!=fir[a][0])
		se=max(se,min(fi,fir[a][0]));
	if(fi!=fir[b][0])
		se=max(se,min(fi,fir[b][0]));
	fi=max(fi,fir[a][0]);
	fi=max(fi,fir[b][0]);
	return make_pair(fi,se);
}
long long ans=0;
long long mn=0x7f7f7f7f;
int main()
{
	scanf("%lld%lld",&n,&m);
	memset(sec,-127/3,sizeof(sec));
	for(long long re i=1;i<=n;i++)fa[i]=i;
	for(long long re i=1;i<=m;i++)
		scanf("%lld%lld%lld",&E[i].u,&E[i].v,&E[i].w);
	sort(E+1,E+m+1,cmp);
	for(long long re i=1;i<=m;i++)
	{
		long long u=E[i].u,v=E[i].v;
		long long w=E[i].w;
		if(find(u)!=find(v))
		{
			fa[find(u)]=find(v);
			add(u,v,w);
			add(v,u,w);
			ans+=w;
			t[i]=1;
		}
	}
	dep[1]=1;
	dfs(1);
	for(long long re i=1;i<=m;i++)
	{
		if(!t[i])
		{
			long long u=E[i].u;
			long long v=E[i].v;
			long long w=E[i].w;
			T t=lca(u,v);
			if(t.first!=w)
				mn=min(mn,w-t.first);
			else mn=min(mn,w-t.second);
		}
	}
	printf("%lld",ans+mn);
}

猜你喜欢

转载自blog.csdn.net/weixin_44111457/article/details/87531611
今日推荐