严格次小生成树

发布时间 2023-10-04 21:49:14作者: fuqingchen

相信读者都已经完全学会最小生成树了……

非严格次小生成树:

一个重要的结论:次小生成树只会改一条边

接着就可以枚举加那条边,设为 $u_i,v_i$,剩下的图一定有一个环,且由 $u_i$ 到 $lca(u_i,v_i)$ 和 $v_i$ 到 $lca(u_i,v_i)$ 构成,我们要删除的边一定是链上最大的边。用倍增 $lca$ 维护最大值即可。

严格次小生成树:

考虑非严格次小生成树的过程,由于可能有边权重复,所以倍增时记录最大值和严格次大值即可

代码(目前来看比许多题解简短许多):

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define mk make_pair
#define pii pair<int, int>
#define f first
#define s second
const int N = 3e5 + 10;
int n, m, lst, sum, mi = 3e14;
vector<pii> vec[N];
bool vis[N];
struct node {
	int u, v, w;
} a[N];
namespace kru {
	int fa[N], sze[N];
	bool cmp(node x, node y) {
		return x.w < y.w;
	}
	int find(int x) {
		if(x == fa[x]) return x;
		else return fa[x] = find(fa[x]);
	}
	void kruskal() {
		for(int i = 1; i <= n; ++i) fa[i] = i, sze[i] = 1;
		sort(a + 1, a + 1 + m, cmp);
		for(int i = 1; i <= m; i++) {
			int u = find(a[i].u), v = find(a[i].v);
			if(u != v) {
				vis[i] = 1;
				sum += a[i].w;
				vec[a[i].u].push_back(mk(a[i].v, a[i].w));
				vec[a[i].v].push_back(mk(a[i].u, a[i].w));
				fa[v] = u;
			}
		}
	}
}
int dep[N], fa[N][21];
pii ma[N][21];
namespace _lca {
	pii merge(pii x, pii y) {
		if (x.f < y.f) return mk(y.f, max(x.f, y.s));
		if (y.f < x.f) return mk(x.f, max(x.s, y.f));
		if (y.f == x.f) return mk(x.f, max(x.s, y.s));
	}
	void dfs(int x, int f, int s) {
		fa[x][0] = f;
		dep[x] = dep[f] + 1;
		ma[x][0] = mk(s, -3e14);
		for(int i = 1; (1 << i) <= dep[x]; ++i)
			fa[x][i] = fa[fa[x][i - 1]][i - 1], ma[x][i] = merge(ma[x][i - 1], ma[fa[x][i - 1]][i - 1]);
		for(pii v : vec[x])
			if(v.f != f) {
				dfs(v.f, x, v.s);
			}
	}
	pii lca(int u, int v) {
		pii res;
		res = mk(-3e14, -3e14);
		if(dep[u] < dep[v]) swap(u, v);
		int k = dep[u] - dep[v];
		for(int i = 20; i >= 0; --i)
			if((k >> i) & 1) res = merge(res, ma[u][i]), u = fa[u][i];
		if(u == v) return res;
		for(int i = 20; i >= 0; --i)
			if(fa[u][i] != fa[v][i]) res = merge(res, merge(ma[u][i], ma[v][i])), u = fa[u][i], v = fa[v][i];
		return merge(res, ma[u][0]);
	}
}
using namespace _lca;

signed main() {
	cin >> n >> m;
	for(int i = 1; i <= m; ++i) cin >> a[i].u >> a[i].v >> a[i].w;
	kru::kruskal();
	for (int i = 0; i <= n; ++i)
		for (int j = 0; j <= 20; ++j) ma[i][j] = mk(-3e14, -3e14);
	memset(dep, 0, sizeof(dep));
	memset(fa, 0, sizeof(fa));
	dfs(1, 0, 0);
	for (int i = 1; i <= m; ++i) {
		if (vis[i]) continue;
		pii p = lca(a[i].u, a[i].v);
		if (p.f == a[i].w) mi = min(mi, sum - p.s + a[i].w);
		else mi = min(mi, sum - p.f + a[i].w);
	}
	printf("%lld", mi);
	return 0;
}