题意:
给定一个有 n 个点、m 条边的无向带权图,求严格次小生成树。(n <= 1e5, m <= 3e5)
链接:
https://vjudge.net/problem/HYSBZ-1977
解题思路:
求非严格次小生成树时,替换路径上的最大边,加入的边权可能与其相等,故倍增时多维护一个路径上严格的次大值即可。
参考代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
#define pb push_back
#define sz(a) ((int)a.size())
#define mem(a, b) memset(a, b, sizeof a)
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define gmid (l + r >> 1)
const int maxn = 3e5 + 5;
const int maxm = 6e6 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
vector<pii> G[maxn];
struct Edge{
int u, v, w;
bool operator < (const Edge &o) const{
return w < o.w;
}
} ei[maxn];
int pre[maxn], dep[maxn], fa[maxn][21];
int mx[maxn][21], mx2[maxn][21];
int n, m, p;
int fin(int x){
return x == pre[x] ? x : pre[x] = fin(pre[x]);
}
ll kruskal(){
sort(ei + 1, ei + 1 + m);
for(int i = 1; i <= n; ++i) G[i].clear(), pre[i] = i;
int cnt = 1; ll ret = 0; p = 0;
for(int i = 1; i <= m; ++i){
int u = ei[i].u, v = ei[i].v, w = ei[i].w;
int x = fin(u), y = fin(v);
if(cnt < n && x != y){
pre[x] = y, ++cnt, ret += w;
G[u].pb({w, v}), G[v].pb({w, u});
}
else ei[++p] = ei[i];
}
return ret;
}
void dfs(int u, int f){
dep[u] = dep[f] + 1;
for(int i = 0; i < sz(G[u]); ++i){
int v = G[u][i].second, w = G[u][i].first;
if(v == f) continue;
fa[v][0] = u, mx[v][0] = w, mx2[v][0] = -1;
for(int i = 1; i <= 20; ++i){
fa[v][i] = fa[fa[v][i - 1]][i - 1];
mx[v][i] = max(mx[v][i - 1], mx[fa[v][i - 1]][i - 1]);
if(mx[v][i - 1] == mx[fa[v][i - 1]][i - 1]) mx2[v][i] = max(mx2[v][i - 1], mx2[fa[v][i - 1]][i - 1]);
else if(mx[v][i - 1] > mx[fa[v][i - 1]][i - 1]) mx2[v][i] = max(mx2[v][i - 1], mx[fa[v][i - 1]][i - 1]);
else mx2[v][i] = max(mx[v][i - 1], mx2[fa[v][i - 1]][i - 1]);
}
dfs(v, u);
}
}
void change(int &ret1, int &ret2, int u, int i){
if(mx[u][i] > ret1) ret2 = max(ret1, mx2[u][i]), ret1 = mx[u][i];
else if(mx[u][i] == ret1) ret2 = max(ret2, mx2[u][i]);
else ret2 = max(ret2, mx[u][i]);
}
pii getMax(int u, int v){
if(dep[u] < dep[v]) swap(u, v);
int ret1 = -1, ret2 = -1;
for(int i = 20; i >= 0; --i){
if(dep[fa[u][i]] >= dep[v]){
change(ret1, ret2, u, i);
u = fa[u][i];
}
}
if(u == v) return {ret1, ret2};
for(int i = 20; i >= 0; --i){
if(fa[u][i] != fa[v][i]){
change(ret1, ret2, u, i);
change(ret1, ret2, v, i);
u = fa[u][i], v = fa[v][i];
}
}
change(ret1, ret2, u, 0);
change(ret1, ret2, v, 0);
return {ret1, ret2};
}
int main(){
// ios::sync_with_stdio(0); cin.tie(0);
scanf("%d%d", &n, &m);
for(int i = 1; i <= m; ++i){
scanf("%d%d%d", &ei[i].u, &ei[i].v, &ei[i].w);
}
ll ret = kruskal(), ret2 = 1ll << 62;
mem(mx[1], -1), mem(mx2[1], -1); dfs(1, 0);
for(int i = 1; i <= p; ++i){
int u = ei[i].u, v = ei[i].v, w = ei[i].w;
pii tmp = getMax(u, v);
if(tmp.first == w && tmp.second == -1) continue;
if(tmp.first != w) ret2 = min(ret2, ret + w - tmp.first);
else ret2 = min(ret2, ret + w - tmp.second);
}
printf("%lld\n", ret2);
return 0;
}