356. 次小生成树

题目描述:
给定一张 N 个点 M 条边的无向图,求无向图的严格次小生成树。

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

输入格式
第一行包含两个整数 N 和 M。

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

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

数据范围
N≤105,M≤3×105
输入样例:

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

输出样例:

11

思路:
本题是朴素版次小生成树的优化版本,相比于原版本,本版本的时空利用率更高,可以处理更多的点。
与朴素版类似,首先先用克鲁斯卡尔算法建树,并标记树边。
接下来用bfs来初始化depth,d1,d2。
其中 d e p t h [ i ] 表 示 i 点 相 对 于 r o o t 的 深 度 , d 1 [ i ] [ k ] 表 示 i 点 向 上 走 2 k 次 的 过 程 中 最 大 边 , d 2 [ i ] [ k ] 则 表 示 i 点 向 上 走 2 k 次 的 过 程 中 的 次 大 边 depth[i]表示i点相对于root的深度,d1[i][k]表示i点向上走2^k次的过程中最大边,d2[i][k]则表示i点向上走2^k次的过程中的次大边 depth[i]irootd1[i][k]i2k,d2[i][k]i2k
不同之处在于,针对d[4]数组。相当于在 a 与 a + 2 k 点 之 间 再 选 一 个 a + 2 k − 1 点 a n c , a 到 a + 2 k 点 中 的 最 大 值 为 d 1 [ j ] [ k − 1 ] , d 2 [ j ] [ k − 1 ] , d 1 [ a n c ] [ k − 1 ] , d 2 [ a n c ] [ k − 1 ] 中 的 一 个 , 次 大 值 同 理 a与a+2^k点之间再选一个a+2^{k-1}点anc,a到a+2^k点中的最大值为{ d1[j][k - 1],d2[j][k - 1],d1[anc][k - 1],d2[anc][k - 1] }中的一个,次大值同理 aa+2ka+2k1ancaa+2kd1[j][k1],d2[j][k1],d1[anc][k1],d2[anc][k1],

在lca中,给出两个点a,b 以及两点间的非树边w。需要找到两点间一条最短的非树边dist,来替换掉原来的树边。
其中dist需要满足在所有a,b之间的树点中,w-dist最小,这样才能保证是最小生成树。
为了达成这个条件,需要倍增得到区间内所有的dist1,dist2,全都加进d数组中。在d数组中,选择出dist1,dist2 。若非树边与dist1相等则用dist2,反之则用dist1。但是若w比dist和dist2都小,则不存在该种情况,返回INF。
代码:

#include<iostream>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
const int N = 1e5 + 10, M = 3e5 + 10, INF = 0x3f3f3f3f;
struct Edge
{
	int u, v, w;
	bool used;
	bool operator<(const Edge & t) const
	{
		return w < t.w;
	}
}edge[M];
int n, m;
int p[N];
int e[M], ne[M], w[M], h[M], idx;
int fa[N][20];
int depth[N];
int d1[N][20], d2[N][20];
void add(int a, int b, int c)
{
	e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}
int find(int x)
{
	if (x == p[x]) 
	{
		return x;
	}
	else
	{
		return p[x] = find(p[x]);
	}
}
long long kruskal()
{
	long long sum = 0;
	for (int i = 1; i <= n; i++)
	{
		p[i] = i;
	}
	for (int i = 0; i < m; i++)
	{
		int u = edge[i].u;
		int v = edge[i].v;
		int w = edge[i].w;
		int a = find(u);
		int b = find(v);
		if (a != b)
		{
			p[a] = b;
			sum += w;
			edge[i].used = true;
			add(u, v, w);
			add(v, u, w);
		}
	}
	return sum;
}
void bfs(int root)
{
	memset(depth, 0x3f, sizeof depth);
	queue<int>q;
	q.push(root);
	depth[root] = 1;
	depth[0] = 0;
	while (!q.empty())
	{
		int t = q.front();
		q.pop();
		for (int i = h[t]; i != -1; i = ne[i])
		{
			int j = e[i];
			if (depth[j] > depth[t] + 1)
			{
				depth[j] = depth[t] + 1;
				fa[j][0] = t;
				d1[j][0] = w[i];
				d2[j][0] = -INF;
				q.push(j);
				for (int k = 1; k < 20; k++)
				{
					int anc = fa[j][k - 1];
					fa[j][k] = fa[anc][k - 1];
					int d[4] = { d1[j][k - 1],d2[j][k - 1],d1[anc][k - 1],d2[anc][k - 1] };
					d1[j][k] = -INF;
					d2[j][k] = -INF;
					for (int u = 0; u < 4; u++)
					{
						int dd = d[u];
						if (dd > d1[j][k])
						{
							d2[j][k] = d1[j][k];
							d1[j][k] = dd;
						}
						else if (dd!=d1[j][k] && dd>d2[j][k])
						{
							d2[j][k] = dd;
						}
					}
				}
			}
		}
	}
}
long long lca(int u, int v, int w)
{
	static int d[N * 2];
	int cnt = 0;
	if (depth[u] < depth[v])
	{
		swap(u, v);
	}
	for (int k = 19; k >= 0; k--)
	{
		if (depth[fa[u][k]] >= depth[v])
		{
			d[cnt++] = d1[u][k];
			d[cnt++] = d2[u][k];
			u = fa[u][k];
		}
	}
	if (u != v)
	{
		for (int k = 19; k >= 0; k--)
		{
			if (fa[u][k] != fa[v][k])
			{
				d[cnt++] = d1[u][k];
				d[cnt++] = d2[u][k];
				d[cnt++] = d1[v][k];
				d[cnt++] = d2[v][k];
				u = fa[u][k];
				v = fa[v][k];
			}
		}
		d[cnt++] = d1[u][0];
		d[cnt++] = d1[v][0];
	}
	int dist1 = -INF;
	int dist2 = -INF;
	for (int i = 0; i < cnt; i++)
	{
		if (d[i] > dist1)
		{
			dist2 = dist1;
			dist1 = d[i];
		}
		else if (d[i]!=dist1 && d[i]>dist2)
		{
			dist2 = d[i];
		}
	}
	if (w > dist1) return w - dist1;
	if (w > dist2) return w - dist2;
	return INF;
}
int main()
{
	memset(h, -1, sizeof h);
	cin >> n >> m;
	for (int i = 0; i < m; i++)
	{
		cin >> edge[i].u >> edge[i].v >> edge[i].w;
	}
	sort(edge, edge + m);
	long long sum = kruskal();
	int root = 1;
	bfs(root);

	long long res = 1e18;
	for (int i = 0; i < m; i++)
	{
		if (!edge[i].used)
		{
			res = min(res, sum+lca(edge[i].u, edge[i].v, edge[i].w));
		}
	}
	cout << res;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值