【严格次小生成树】【模板】

Luogu P4180

严格次小生成树

∑ w 次 > ∑ w 最 \sum w_{次}>\sum w_{最} w>w
建好最小生成树后,改动一下得到
枚举每条非树边,替换树中的最大值
但是由于可能算出来和最小生成树一样,就不是严格的了。于是我们需要维护最大和严格次大。用倍增维护即可

#include<bits/stdc++.h>
#define ll long long
#define int long long
using namespace std;
const int N=1e5+10,M=6e5+10;
const ll INF=1e18;
int n,m;
ll sum,ans;
int head[N],nex[M],to[M],val[M],tot;
void build(int u,int v,int w)
{
	tot++;nex[tot]=head[u];to[tot]=v;val[tot]=w;head[u]=tot;
}
struct Road{
	int x,y,z;
	bool operator < (const Road x)const{
		return z<x.z;
	}
}a[M];
int f[N],pos[M];
int fa(int x){return x==f[x]?x:f[x]=fa(f[x]);}
void kruscal()
{
	for(int i=1;i<=n;i++)f[i]=i;
	sort(a+1,a+m+1);
	for(int i=1;i<=m;i++)
	{
		int x=fa(a[i].x),y=fa(a[i].y);
		if(x!=y)
		{
			f[x]=y;sum+=a[i].z;pos[i]=1;
			build(a[i].x,a[i].y,a[i].z);
			build(a[i].y,a[i].x,a[i].z);
		}
	}
}
int dep[N],bz[N][20];
ll bm1[N][20],bm2[N][20];
void dfs(int u,int fr)
{
	bz[u][0]=fr;dep[u]=dep[fr]+1;
	for(int i=head[u];i;i=nex[i])
	{
		int v=to[i];if(v==fr)continue;
		dfs(v,u);bm1[v][0]=bm2[v][0]=val[i];
	}
}
ll ls[5];int s;
void updata(ll &g1,ll &g2,ll a1,ll a2,ll a3,ll a4)
{
	ls[1]=a1;ls[2]=a2;ls[3]=a3;ls[4]=a4;
	sort(ls+1,ls+5);
	s=3;while(s&&ls[s]==ls[s+1])s--;
	g1=ls[4];g2=ls[s];
}
void get_bz()
{
	dfs(1,0);
	for(int j=1;j<=18;j++)
		for(int i=1;i<=n;i++)
		{
			bz[i][j]=bz[bz[i][j-1]][j-1];
			updata(bm1[i][j],bm2[i][j],bm1[i][j-1],bm1[bz[i][j-1]][j-1],bm2[i][j-1],bm2[bz[i][j-1]][j-1]);
		}
}
ll m1,m2;
int lca(int x,int y)
{
	m1=0,m2=0;
	if(dep[x]<dep[y])swap(x,y);
	for(int i=18;i>=0;i--)
	if(dep[bz[x][i]]>=dep[y])
	{
		updata(m1,m2,m1,m2,bm1[x][i],bm2[x][i]);
		x=bz[x][i];
	}
	if(x==y)return x;
	for(int i=18;i>=0;i--)
	if(bz[x][i]^bz[y][i])
	{
		updata(m1,m2,m1,m2,bm1[x][i],bm2[x][i]);
		updata(m1,m2,m1,m2,bm1[y][i],bm2[y][i]);
		x=bz[x][i],y=bz[y][i];		
	}
	updata(m1,m2,m1,m2,bm1[x][0],bm2[x][0]);
	updata(m1,m2,m1,m2,bm1[y][0],bm2[y][0]);
	return bz[x][0];
}
void get_M2tree()
{
	ans=INF;
	for(int i=1;i<=m;i++)
	{
		if(!pos[i])
		{
			lca(a[i].x,a[i].y);
			if(a[i].z!=m1)
			ans=min(ans,sum-m1+a[i].z);
			ans=min(ans,sum-m2+a[i].z);
		}
	}
}
signed main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	scanf("%lld%lld%lld",&a[i].x,&a[i].y,&a[i].z);
	kruscal();
	get_bz();
	get_M2tree();
	printf("%lld",ans);
}

再次吐槽为什么每次 咕站模板题都会T呀 T^T

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值