严格次小生成树
给定一个带权无向图,求出该图严格次小生成树的长度。
树上倍增 LCA
考虑到任意一棵非最小生成树一定与最小生成树有至少一条边不相同,且只有一条边不相同时取到最优情况。
于是我们可以枚举加上不在最小生成树上的一条边 \(u\leftrightarrow v\),而这时图会出现环,还需要再删掉最小生成树 \(u,v\) 路径上的一条边。贪心选择删掉边权最大的那一条,可以让得到的生成树尽量小。
两点间边权最大的那条边可以用树上倍增 LCA 得到,只需再记录到 \(2^j\) 级祖先的边权最大值即可。因为要求生成树严格次小,要删掉的可能不是边权最大的那条,所以还需记录到 \(2^j\) 级祖先的边权严格次大值。
#include <bits/stdc++.h>
using namespace std;
#define MAXN 100010
#define MAXM 300010
#define LOGN 25
int n, m, s, sum, ans = 1e9, dis[MAXN], fa[MAXN], p[MAXN][LOGN], q[MAXN][LOGN], z[MAXN][LOGN]; // q 最大值,z 次大值
int find(int u) { if (u == fa[u]) return u; else return fa[u] = find(fa[u]); }
struct node {
int u, v, w; bool b;
node() { b = 0; }
node(int x, int y, int z) { u = x, v = y, w = z, b = 0; }
} e[MAXM]; vector<pair<int, int> > g[MAXN];
bool comp(node x, node y) { return x.w < y.w; }
void dfs(int u, int f) {
dis[u] = dis[f] + 1;
for (int i = 0; i < g[u].size(); i++) {
int v = g[u][i].first, w = g[u][i].second;
if (f == v) continue;
p[v][0] = u; q[v][0] = w;
for (int j = 1; j <= 20; j++) {
p[v][j] = p[p[v][j - 1]][j - 1],
q[v][j] = max(q[v][j - 1], q[p[v][j - 1]][j - 1]);
if (q[v][j - 1] < q[p[v][j - 1]][j - 1]) z[v][j] = q[v][j - 1];
else if (q[v][j - 1] < q[p[v][j - 1]][j - 1]) z[v][j] = max(z[v][j], q[p[v][j - 1]][j - 1]);
z[v][j] = max(z[v][j], max(z[v][j - 1], z[p[v][j - 1]][j - 1]));
}
dfs(g[u][i].first, u);
}
}
int cmp(int u, int i, int w) {
if (q[u][i] < w) return q[u][i];
return z[u][i];
}
int lca(int u, int v, int w) {
if (dis[u] < dis[v]) swap(u, v);
int res = -1e9;
for (int i = 20; i >= 0; i--)
if (dis[p[u][i]] >= dis[v]) res = max(res, cmp(u, i, w)), u = p[u][i];
if (u == v) return res;
for (int i = 20; i >= 0; i--)
if (p[u][i] != p[v][i]) res = max(res, max(cmp(u, i, w), cmp(v, i, w))), u = p[u][i], v = p[v][i];
res = max(res, max(cmp(u, 0, w), cmp(v, 0, w)));
}
signed main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) for (int j = 0; j <= 20; j++) q[i][j] = -1e9, z[i][j] = -1e9;
for (int i = 1; i <= n; i++) fa[i] = i;
for (int i = 1; i <= m; i++) {
int u, v, w; cin >> u >> v >> w; e[i] = node(u, v, w);
}
sort(e + 1, e + m + 1, comp);
for (int i = 1; i <= m; i++)
if (find(e[i].u) != find(e[i].v)) {
g[e[i].u].push_back(make_pair(e[i].v, e[i].w));
g[e[i].v].push_back(make_pair(e[i].u, e[i].w));
fa[find(e[i].u)] = find(e[i].v); s = e[i].u;
e[i].b = 1; sum += e[i].w;
}
dfs(s, 0);
for (int i = 1; i <= m; i++) if (!e[i].b) {
if (e[i].u == e[i].v) continue;
int qwq = sum - lca(e[i].u, e[i].v, e[i].w) + e[i].w;
ans = min(ans, qwq);
}
cout << ans << endl;
return 0;
}