严格次小生成树

Problem

在这里插入图片描述

Solution

  • 做法与求非严格次小生成树类似(枚举非mst上的边来替换mst上的边)。
  • 需要同时维护最大边和严格次大边的倍增数组,不存在严格次大边的情况下需设为-inf,保证此次计算不会作为最终答案。
  • 关于严格次大边倍增数组的递推:取左右区间严格次大边的max,同时若左右区间的最大边不相等,两区间最大边的最小值。
  • 关于求树上两点间的严格次大边:假设待加入边权值为w,每次更新时若区间最大边<w,则用区间最大边更新,否则用区间的严格次大边更新。

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
const int maxn=2e5,maxm=300005;
const int inf = 1e9+5;
int t,n,m,cnt,logg[maxn],f[maxn],pre[maxn][35],dep[maxn];
long long mx[maxn][35],fmx[maxn][35],mst;
bool used[maxm];
struct edge{
	int u,v,w;
}e[maxm];
struct node{
	int u,w;
};
vector<node>q[maxn];
bool cmp(const edge &a,const edge &b)
{
	return a.w<b.w;
}
inline void add(int u,int v,int w)
{
	e[++cnt].v=v;
	e[cnt].w=w;
	e[cnt].u=u;
}
int find(int x)
{
	return f[x]==x?x:f[x]=find(f[x]);
}
void kruskal()
{
	int k = 0;
	sort(e+1,e+1+m,cmp);
	for(int i=1;i<=m;i++)
	{
		int u=e[i].u,v=e[i].v,w=e[i].w;
		if(find(u)!=find(v))
		{
			k++;
			mst+=w;
			f[find(u)]=f[find(v)];
			used[i] = 1;
			q[u].push_back({v,w});
			q[v].push_back({u,w});
			if(k==n-1) return ;
		}
	}
}
void dfs(int u,int fa)
{
	for(int i=1;i<=18;i++)
	{
		pre[u][i]=pre[pre[u][i-1]][i-1];
		mx[u][i]=max(mx[u][i-1],mx[pre[u][i-1]][i-1]);
		fmx[u][i]=max(fmx[u][i-1],fmx[pre[u][i-1]][i-1]);
		if(mx[u][i-1]>mx[pre[u][i-1]][i-1]) fmx[u][i]=max(fmx[u][i],mx[pre[u][i-1]][i-1]);
		else if(mx[u][i-1]<mx[pre[u][i-1]][i-1]) fmx[u][i]=max(fmx[u][i],mx[u][i-1]);
	}
	for(int i=0;i<q[u].size();i++)
	{
		int v=q[u][i].u,w=q[u][i].w;
		if(v!=fa)
		{
			mx[v][0]=w;
			fmx[v][0]=-inf;
			pre[v][0]=u;
			dep[v]=dep[u]+1; 
			dfs(v,u);
		}
	}
}
long long lcaw(int x,int y,int w)
{
	long long maxw = -1e18;
    if(dep[x]<dep[y])
    swap(x,y);
    while(dep[x]!=dep[y])
    {
    	if(mx[x][logg[dep[x]-dep[y]]]!=w)
    	maxw=max(maxw,mx[x][logg[dep[x]-dep[y]]]);
    	else
    	maxw=max(maxw,fmx[x][logg[dep[x]-dep[y]]]);
    	x=pre[x][logg[dep[x]-dep[y]]];
	}
    if(x==y)
    return maxw;
    for(int k=logg[dep[x]];k>=0;k--)
    if(pre[x][k]!=pre[y][k])
    {
    	maxw=mx[x][k]!=w?max(maxw,mx[x][k]):max(maxw,fmx[x][k]);
    	maxw=mx[y][k]!=w?max(maxw,mx[y][k]):max(maxw,fmx[y][k]);
    	x=pre[x][k],y=pre[y][k];
	}
	maxw=mx[x][0]!=w?max(maxw,mx[x][0]):maxw;
	maxw=mx[y][0]!=w?max(maxw,mx[y][0]):maxw;
    return maxw;
}
long long SMST()
{
	long long smst = 1e18;
	for(int i=1;i<=m;i++)
	{
		int u=e[i].u,v=e[i].v,w=e[i].w;
		if(!used[i])
			smst=min(smst,mst+w-lcaw(u,v,w));
	}
	return smst;
}
int main()
{
	t = 1;
	for(int i=2;i<=100005;i++)
		logg[i]=logg[i-1]+((1<<(logg[i-1]+1))==i);
	while(t--)
	{
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n;i++)
		f[i]=i;
		for(int i=1,u,v,w;i<=m;i++)
		{
			scanf("%d%d%d",&u,&v,&w);
			add(u,v,w);
		}
		kruskal();
		dfs(1,0);
		cout<<SMST()<<endl;
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

哈希表扁豆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值