有定理

存在次小生成树,只替换最小生成树的一条边得来。

那么先跑一遍最小生成树

然后枚举不在树边的边 ( u , v ) (u,v) (u,v)

假如加入这条边,树就会成环,我们需要删掉 u − > v u->v u>v的一条树边

显然删掉最大的那条边最优秀

这个过程使用 L C A LCA LCA来优化

严格次小生成树模板题

带权 L C A LCA LCA

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=2e5+10; 
const int inf=1e18;
int n,m;
int pre[maxn],deep[maxn],lg[maxn],ok[maxn];
int fa[maxn][21],maxx[maxn][21],ci[maxn][21];
struct p{
	int l,r,w;
	bool operator < (const p&tmp ){
		return w<tmp.w;
	}
}a[maxn<<2];
struct edge{
	int to,nxt,w;
}d[maxn<<2]; int head[maxn<<2],cnt=1;
void add(int u,int v,int w){d[++cnt] = (edge){v,head[u],w},head[u]=cnt;}
int find(int x){ return x==pre[x]?x:pre[x]=find(pre[x]);}
int kur()
{
	sort(a+1,a+1+m);
	for(int i=1;i<=n;i++)	pre[i]=i;
	int ans=0;
	for(int i=1,j=1;i<n;i++)
	for(;j<=m;j++)
	{
		int fl=find(a[j].l),fr=find(a[j].r),w=a[j].w;
		if( fl!=fr )
		{
			add(a[j].l,a[j].r,w); add(a[j].r,a[j].l,w);
			pre[fl]=fr, ans+=w; ok[j]=1;
			break;
		}
	}
	return ans;
}
void dfs(int u,int father)
{
	deep[u]=deep[father]+1,fa[u][0]=father;
	for(int i=1;i<=lg[deep[u]]+1;i++)
	{
		int r = fa[u][i-1];
		fa[u][i]=fa[r][i-1];
		maxx[u][i]=max( maxx[u][i-1],maxx[r][i-1] );
		ci[u][i]=max( ci[u][i],ci[r][i-1] );
		if( maxx[u][i-1]>maxx[r][i-1] )	
			ci[u][i] = max( ci[u][i],maxx[r][i-1] );
		else if( maxx[u][i-1]<maxx[r][i-1] )
			ci[u][i] = max( ci[u][i],maxx[u][i-1] );
	}
	for(int i=head[u];i;i=d[i].nxt )
	{
		int v=d[i].to;
		if( v==father )	continue;
		maxx[v][0]=d[i].w;
		ci[v][0]=-inf;
		dfs(v,u);
	}
}
int LCA(int x,int y,int w)
{
	int ans = -inf;
	if( deep[x]<deep[y] )	swap(x,y);
	for(int i=20;i>=0;i--)
		if( deep[fa[x][i]]>=deep[y] )
		{
			if( w!=maxx[x][i] )	ans = max(ans,maxx[x][i] );
			else	ans = max(ans,ci[x][i] );
			x = fa[x][i];
		}
	if( x==y )	return ans;
	for(int i=20;i>=0;i--)
		if( fa[x][i]!=fa[y][i] )
		{	
			if( w!=maxx[x][i] )	ans = max(ans,maxx[x][i] );
			else	ans = max(ans,ci[x][i] );
			if( w!=maxx[y][i] )	ans = max(ans,maxx[y][i] );
			else	ans = max(ans,ci[y][i] );
			x=fa[x][i],y=fa[y][i];
		}
	if( w!=maxx[x][0] )	ans = max(ans,maxx[x][0] );
	else	ans = max(ans,ci[x][0] );
	if( w!=maxx[y][0] )	ans = max(ans,maxx[y][0] );
	else	ans = max(ans,ci[y][0] );	
	return ans;
}
void init()
{
	for(int i=1;i<=n;i++)
		lg[i] = lg[i-1]+((1<<lg[i-1])==i);
}
signed main()
{
	cin >> n >> m;
	init();
	for(int i=1;i<=m;i++)
		cin >> a[i].l >> a[i].r >> a[i].w;
	int chu = kur();
	ci[1][0]=-inf;
	dfs(1,0);
	int ans = inf;
	for(int i=1;i<=m;i++)
	{
		if( ok[i] )	continue;
		int u = a[i].l,v = a[i].r, w = a[i].w;
		ans = min( ans,chu-LCA(u,v,w)+w );
	}
	cout << ans;
}

先求 l c a lca lca再跳,其实都差不多

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=2e5+10; 
const int inf=1e18;
int n,m;
int pre[maxn],deep[maxn],lg[maxn],ok[maxn];
int fa[maxn][21],maxx[maxn][21],ci[maxn][21];
struct p{
	int l,r,w;
	bool operator < (const p&tmp ){
		return w<tmp.w;
	}
}a[maxn<<2];
struct edge{
	int to,nxt,w;
}d[maxn<<2]; int head[maxn<<2],cnt=1;
void add(int u,int v,int w){d[++cnt] = (edge){v,head[u],w},head[u]=cnt;}
int find(int x){ return x==pre[x]?x:pre[x]=find(pre[x]);}
int kur()
{
	sort(a+1,a+1+m);
	for(int i=1;i<=n;i++)	pre[i]=i;
	int ans=0;
	for(int i=1,j=1;i<n;i++)
	for(;j<=m;j++)
	{
		int fl=find(a[j].l),fr=find(a[j].r),w=a[j].w;
		if( fl!=fr )
		{
			add(a[j].l,a[j].r,w); add(a[j].r,a[j].l,w);
			pre[fl]=fr, ans+=w; ok[j]=1;
			break;
		}
	}
	return ans;
}
void dfs(int u,int father)
{
	deep[u]=deep[father]+1,fa[u][0]=father;
	for(int i=1;i<=lg[deep[u]];i++)
	{
		int r = fa[u][i-1];
		fa[u][i]=fa[r][i-1];
		maxx[u][i]=max( maxx[u][i-1],maxx[r][i-1] );
		ci[u][i]=max( ci[u][i],ci[r][i-1] );
		if( maxx[u][i-1]>maxx[r][i-1] )	
			ci[u][i] = max( ci[u][i],maxx[r][i-1] );
		else if( maxx[u][i-1]<maxx[r][i-1] )
			ci[u][i] = max( ci[u][i],maxx[u][i-1] );
	}
	for(int i=head[u];i;i=d[i].nxt )
	{
		int v=d[i].to;
		if( v==father )	continue;
		maxx[v][0]=d[i].w;
		ci[v][0]=-inf;
		dfs(v,u);
	}
}
int LCA(int x,int y)
{
	if( deep[x]<deep[y] )	swap(x,y);
	for(int i=20;i>=0;i--)
		if( deep[fa[x][i]]>=deep[y] )	x=fa[x][i];
	if( x==y )	return x;
	for(int i=20;i>=0;i--)
		if( fa[x][i]!=fa[y][i] )	
			x=fa[x][i],y=fa[y][i];
	return fa[x][0];
}
int make_max(int x,int y,int da)
{
	int ans = -inf;
	for(int i=20;i>=0;i--)
		if( deep[fa[x][i]]>=deep[y] )
		{
			if( da!=maxx[x][i] )	ans =max( ans,maxx[x][i] );
			else	ans = max( ans,ci[x][i] );
			x = fa[x][i];
		}
	return ans;
}
void init()
{
	for(int i=1;i<=n;i++)
		lg[i] = lg[i-1]+((1<<lg[i-1])==i);
}
signed main()
{
	cin >> n >> m;
	init();
	for(int i=1;i<=m;i++)
		cin >> a[i].l >> a[i].r >> a[i].w;
	int chu = kur();
	ci[1][0]=-inf;
	dfs(1,0);
	int ans = inf;
	for(int i=1;i<=m;i++)
	{
		if( ok[i] )	continue;
		int u = a[i].l,v = a[i].r, w = a[i].w;
		int lca = LCA(u,v);
		int q=make_max(u,lca,w), e = make_max(v,lca,w);
		ans = min( ans,chu-max(q,e)+w );
	}
	cout << ans;
}

非严格次小生成树

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
using namespace std;
const int maxn = 2e5+10;
const int inf = 1e9;
struct p{
	int l,r,w;
	bool operator < (const p&tmp )	const{
		return w<tmp.w;
	} 
}a[maxn];
struct edge{
	int to,nxt,w;
}d[maxn]; int head[maxn],cnt=1;
void add(int u,int v,int w){
	d[++cnt] = (edge){v,head[u],w},head[u] = cnt;
}
int fa[maxn][22],deep[maxn],maxx[maxn][22],pre[maxn],n,m,ok[maxn];
int find(int x){ return x==pre[x]?x:pre[x]=find(pre[x]);}
int kur()
{
	int ans = 0;
	for(int i=1;i<=n;i++)	pre[i] = i;
	sort( a+1,a+1+m );
	for(int i=1;i<=m;i++)
	{
		int fl = find(a[i].l), fr = find(a[i].r);
		if( fl==fr )	continue;
		pre[fl] = fr; ok[i] = 1;
		add( a[i].l,a[i].r,a[i].w ); add( a[i].r,a[i].l,a[i].w );
		ans += a[i].w;
	}
	return ans;
}
void dfs(int u,int father)
{
	fa[u][0]=father, deep[u] = deep[father]+1;
	for(int i=1;i<=20;i++)
	{
		int r = fa[u][i-1];
		fa[u][i] = fa[r][i-1];
		maxx[u][i] = max( maxx[u][i-1],maxx[r][i-1] );
	}
	for(int i=head[u];i;i=d[i].nxt )
	{
		int v = d[i].to;
		if( v==father )	continue;
		maxx[v][0] = d[i].w;
		dfs(v,u);
	}
}
int lca(int x,int y)
{
	if( deep[x]<deep[y] )	swap(x,y);
	int ans = -inf;
	for(int i=20;i>=0;i--)
		if( deep[fa[x][i]]>=deep[y] )
		{
			ans = max( ans,maxx[x][i] );
			x = fa[x][i];
		}
	if( x==y )	return ans;
	for(int i=20;i>=0;i--)
		if( fa[x][i]!=fa[y][i] )
		{
			ans = max( ans,max(maxx[x][i],maxx[y][i]) );
			x = fa[x][i], y = fa[y][i];
		}
	ans = max( ans,max( maxx[x][0],maxx[y][0]) );
	return ans;
}
void init()
{
	for(int i=1;i<=m;i++)	ok[i] = 0;
	cnt = 1;
	for(int i=1;i<=n;i++)	head[i] = 0;
}
int main()
{
	int T; cin >> T;
	while( T-- )
	{
		scanf("%d%d",&n,&m);
		for(int i=1;i<=m;i++)
			cin >> a[i].l >> a[i].r >> a[i].w;
		int chu = kur(),ans = inf;
		dfs(1,0);
		for(int i=1;i<=m;i++)
		{
			if( ok[i] )	continue;
			ans = min( ans , chu-lca(a[i].l,a[i].r)+a[i].w );
		}
//		if( chu==ans )	cout << "Not Unique!\n";
//		else	cout << chu << endl;
		init();
	}
}