【题解 P4180】严格次小生成树

[BJWC2010] 严格次小生成树

题目描述

小 C 最近学了很多最小生成树的算法,Prim 算法、Kruskal 算法、消圈算法等等。正当小 C 洋洋得意之时,小 P 又来泼小 C 冷水了。小 P 说,让小 C 求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说:如果最小生成树选择的边集是 \(E_M\),严格次小生成树选择的边集是 \(E_S\),那么需要满足:(\(value(e)\) 表示边 \(e\) 的权值) \(\sum_{e \in E_M}value(e)<\sum_{e \in E_S}value(e)\)

这下小 C 蒙了,他找到了你,希望你帮他解决这个问题。

输入格式

第一行包含两个整数 \(N\)\(M\),表示无向图的点数与边数。

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

输出格式

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

样例 #1

样例输入 #1

5 6
1 2 1 
1 3 2 
2 4 3 
3 5 4 
3 4 3 
4 5 6

样例输出 #1

11

提示

数据中无向图不保证无自环

对于 \(50\%\) 的数据, \(N\le 2000\)\(M\le 3000\)

对于 \(80\%\) 的数据, \(N\le 5\times 10^4\)\(M\le 10^5\)

对于 \(100\%\) 的数据, \(N\le 10^5\)\(M\le 3\times10^5\),边权 \(\in [0,10^9]\),数据保证必定存在严格次小生成树。

原题地址

解法

题意还是很容易理解的,求严格最小的生成树。
很经典的一个想法,跑 \(kruskal\) 后最小生成树维护两点之间路径上最大的权值,枚举每条未放进最小生成树的边,拿最小生成树权值和减去路径最大权值再加上这条边的权值即可。
结果打完发现连样例都没过。
观察样例后发现,路径上存在与这条边权值相同的边,而题目是严格最小。
所以, \(LCA\) 时还要维护严格次小的边。
当路径上最大权值与此边权值相等时,就用次小权值来。
证明显然,只可能改掉一条边,因为原来是最小生成树,每次改边都会增大权值,改两边不如改一边优。
删掉最大或次大边,可以使剩下选的边权值和最小,从而使加上枚举的边后权值和最小。

代码

#include<bits/stdc++.h>
using namespace std;
struct datay
{
	long long x,y,z;
}l[300005];
long long n,m,d[300005],f[300005][21],deep[300005],t[300005][21],val=0,t1[300005][21],val1;
bool v[300005];
vector<long long> a[300005],r[300005];
long long dijah(long long x)
{
	if(d[x]!=x)d[x]=dijah(d[x]);
	return d[x];
}
void dfs(long long x,long long y)
{
	deep[x]=deep[y]+1;
	f[x][0]=y;
	for(int i=0;i<a[x].size();i++)
	{
		if(a[x][i]==y)continue;
		t[a[x][i]][0]=r[x][i];
		dfs(a[x][i],x);
	}
	return;
}
bool cmp(datay q,datay w)
{
	return q.z<w.z;
}
long long LCA(long long x,long long y)
{
	val=0;
	val1=0;
	if(deep[x]<deep[y])swap(x,y);
	for(int i=20;i>=0;i--)
	{
		if(deep[f[x][i]]>=deep[y])
		{
			if(val1<=t1[x][i])
			{
				if(t1[x][i]>val)val1=t1[x][i];
				else if(t[x][i]>val)val1=val;
				else if(t[x][i]!=val)val1=t[x][i];
				else val1=max(t1[x][i],val1);
			}
			else if(t1[x][i]<val1&&t[x][i]>=val1)val1=t[x][i]==val?val1:min(val,t[x][0]);
			val=max(val,t[x][i]),x=f[x][i];
		}
	} 
	if(x==y)return x;
	for(int i=20;i>=0;i--)
	{
		if(f[x][i]!=f[y][i])
		{
			if(val1<=t1[x][i])
			{
				if(t1[x][i]>val)val1=t1[x][i];
				else if(t[x][i]>val)val1=val;
				else if(t[x][i]!=val)val1=t[x][i];
				else val1=max(t1[x][i],val1);
			}
			else if(t1[x][i]<val1&&t[x][i]>=val1)val1=t[x][i]==val?val1:min(val,t[x][0]);
			val=max(val,t[x][i]);
			if(val1<=t1[y][i])
			{
				if(t1[y][i]>val)val1=t1[y][i];
				else if(t[y][i]>val)val1=val;
				else if(t[y][i]!=val)val1=t[y][i];
				else val1=max(t1[y][i],val1);
			}
			else if(t1[y][i]<val1&&t[y][i]>=val1)val1=t[y][0]==val?val1:min(val,t[y][0]);
			val=max(val,t[y][i]);
			x=f[x][i];
			y=f[y][i];
		}
	}
	if(val>t[x][0])val1=t[x][0];
	val=max(val,t[x][0]);
	if(val>t[y][0])val1=t[y][0];
	val=max(val,t[y][0]);
	return f[x][0];
}
int main()
{
	long long x,y,s=0,minn=1e15+5;
	memset(v,false,sizeof(v));
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%lld%lld%lld",&l[i].x,&l[i].y,&l[i].z);
	}
	for(int i=1;i<=m;i++)d[i]=i;
	sort(l+1,l+m+1,cmp);
	for(int i=1;i<=m;i++)
	{
		x=dijah(l[i].x),y=dijah(l[i].y);
		if(x!=y)
		{
			a[l[i].x].push_back(l[i].y);
			a[l[i].y].push_back(l[i].x);
			r[l[i].x].push_back(l[i].z);
			r[l[i].y].push_back(l[i].z);
			d[y]=x;
			s+=l[i].z;
			v[i]=true;
		}
	}
	dfs(1,0);
	for(int i=1;i<=20;i++)
	{
		for(int j=1;j<=n;j++)
		{
			f[j][i]=f[f[j][i-1]][i-1];
			t[j][i]=max(t[j][i-1],t[f[j][i-1]][i-1]);
			if(t[j][i-1]!=t[f[j][i-1]][i-1])t1[j][i]=min(t[j][i-1],t[f[j][i-1]][i-1]);
			else
			{
				t1[j][i]=max(t1[j][i-1],t1[f[j][i-1]][i-1]);
			}
		}
	}
	for(int i=1;i<=m;i++)
	{
		if(v[i]||l[i].x==l[i].y)continue;
		LCA(l[i].x,l[i].y);
		if(val!=l[i].z)minn=min(minn,s-val+l[i].z);
		if(val1!=l[i].z)minn=min(minn,s-val1+l[i].z);
	}
	printf("%lld",minn);
	







  return 0;
}
posted @ 2023-08-28 15:09  dijah  阅读(69)  评论(0编辑  收藏  举报