严格次小生成树
相信读者都已经完全学会最小生成树了……
非严格次小生成树:
一个重要的结论:次小生成树只会改一条边
接着就可以枚举加那条边,设为 $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; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效