356. 次小生成树

题目链接

356. 次小生成树

给定一张 \(N\) 个点 \(M\) 条边的无向图,求无向图的严格次小生成树。

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

输入格式

第一行包含两个整数 \(N\)\(M\)

接下来 \(M\) 行,每行包含三个整数 \(x,y,z\),表示点 \(x\) 和点 \(y\) 之前存在一条边,边的权值为 \(z\)

输出格式

包含一行,仅一个数,表示严格次小生成树的边权和。(数据保证必定存在严格次小生成树)

数据范围

\(N≤10^5,M≤3×10^5\)

输入样例:

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

输出样例:

11

解题思路

倍增,最小生成树

先求出最小生成树,最小生成树中的边称为“树边”,对于每一条“非树边”,计算加上这条边后的最少代价,即加上这条边后会形成一个环,当权值最大的边的权值与当当前“非树边”的权值不相等时,需要在环去掉这条权值最大的边,否则去掉权值次大的边。所以关键在于寻找最小生成树中两点之间权值最大和次大的边,可类比LCA:

  • 状态表示:\(dp[i][j][k]\) 表示 \(j\) 向上走 \(2^k\) 步的最大值(\(i=0\))/次大值(\(i=1\)

  • 状态计算:

  • \(dp[0][y][i-1]==dp[0][f[y][i-1]][i-1]\),其中 \(f[i][j]\) 表示 \(i\) 向上走 \(2^j\) 步到达的节点

    • \(dp[0][y][i]=dp[0][y][i-1]\)
    • \(dp[1][y][i]=max(dp[1][y][i-1],dp[1][f[y][i-1]][i-1])\)
  • \(dp[0][y][i-1]\neq dp[0][f[y][i-1]][i-1]\)

    • \(dp[0][y][i]=max(dp[0][y][i-1],dp[0][f[y][i-1]][i-1])\)
    • \(dp[1][y][i]=max(\{min(dp[0][y][i-1],dp[0][f[y][i-1]][i-1]),dp[1][y][i-1],dp[1][f[y][i-1]][i-1]\})\)
      分析:整体最大值为前后两部分的最大值,当前后两部分的最大值相等时,次大值为前后两部分次大值的较大值;否则为前后两部分最大值的较小值和前后两部分的次大值的最大值

最后求最小生成树两点的最大和次大值可类比LCA

  • 时间复杂度:\(O(m(logn+logm))\)

代码

// Problem: 次小生成树
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/description/358/
// Memory Limit: 512 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

// %%%Skyqwq
#include <bits/stdc++.h>
 
// #define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
 
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
 
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
 
template <typename T> void inline read(T &x) {
    int f = 1; x = 0; char s = getchar();
    while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
    while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
    x *= f;
}


const int N=3e5+5;
const LL inf=1e16;
int n,m,fa[N],d[N],f[N][20];
LL dp[2][N][20],val1,val2,res,ret;
bool v[N];
vector<PII> adj[N];
struct T
{
	int x,y,z;
	bool operator<(const T &t)
	{
		return z<t.z;
	}
}tr[N];
int find(int x)
{
	return x==fa[x]?x:fa[x]=find(fa[x]);
}
void kruskal()
{
    sort(tr+1,tr+1+m);
	for(int i=1;i<=m;i++)
	{
		int x=tr[i].x,y=tr[i].y,z=tr[i].z;
		int a=find(x),b=find(y);
		if(a==b)continue;
		res+=z;
		adj[x].pb({y,z});
		adj[y].pb({x,z});
		v[i]=true;
		fa[a]=b;
	}
}
void bfs()
{
	d[1]=0;
	queue<int> q;
	q.push(1);
	while(q.size())
	{
		int x=q.front();
		q.pop();
		int len=log2(d[x]);
		for(auto t:adj[x])
		{
			int y=t.fi,z=t.se;
			if(y==f[x][0])continue;
			q.push(y);
			f[y][0]=x;
			d[y]=d[x]+1;
			dp[0][y][0]=z,dp[1][y][0]=-inf;
			for(int i=1;i<=len;i++)
			{
				f[y][i]=f[f[y][i-1]][i-1];
				if(dp[0][y][i-1]==dp[0][f[y][i-1]][i-1])
				{
					dp[0][y][i]=dp[0][y][i-1];
					dp[1][y][i]=max(dp[1][y][i-1],dp[1][f[y][i-1]][i-1]);
				}
				else
				{
					dp[0][y][i]=max(dp[0][y][i-1],dp[0][f[y][i-1]][i-1]);
					dp[1][y][i]=max({min(dp[0][y][i-1],dp[0][f[y][i-1]][i-1]),dp[1][y][i-1],dp[1][f[y][i-1]][i-1]});
				}
			}
		}
	}
}
void update(int x)
{
	if(val1<x)val2=val1,val1=x;
	else if(val2<x&&x!=val1)val2=x;
}
void lca(int x,int y)
{
	val1=val2=-inf;
	if(d[x]>d[y])swap(x,y);
	while(d[x]<d[y])
	{
		int len=log2(d[y]-d[x]);
		update(dp[0][y][len]);
		update(dp[1][y][len]);
		y=f[y][len];
	}
	if(x==y)return ;
	for(int len=log2(d[x]);len>=0;len--)
		if(f[x][len]!=f[y][len])
		{
			update(dp[0][x][len]);
			update(dp[1][x][len]);
			update(dp[0][y][len]);
			update(dp[1][y][len]);
			x=f[x][len];
			y=f[y][len];
		}
	update(dp[0][x][0]);
	update(dp[1][x][0]);
	update(dp[0][y][0]);
	update(dp[1][y][0]);
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)fa[i]=i;
    for(int i=1;i<=m;i++)
    {
    	int x,y,z;
    	scanf("%d%d%d",&x,&y,&z);
    	tr[i]={x,y,z};
    }
    kruskal();
    bfs();
    ret=inf;
    for(int i=1;i<=m;i++)
    {
    	if(v[i])continue;
    	lca(tr[i].x,tr[i].y);
    	if(tr[i].z==val1)
    		ret=min(ret,res-val2+tr[i].z);
    	else
    		ret=min(ret,res-val1+tr[i].z);
    }
    printf("%lld",ret);
    return 0;
}
posted @ 2022-03-22 14:10  zyy2001  阅读(27)  评论(0编辑  收藏  举报