小 C 最近学了很多最小生成树的算法,Prim 算法、Kurskal 算法、消圈算法等等。 正当小 C 洋洋得意之时,小 P 又来泼小 C 冷水了。小 P 说,让小 C 求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说: 如果最小生成树选择的边集是 EM,严格次小生成树选择的边集是 ES,那么需要满足:(value(e) 表示边 e的权值) 这下小 C 蒙了,他找到了你,希望你帮他解决这个问题。
第一行包含两个整数N 和M,表示无向图的点数与边数。 接下来 M行,每行 3个数x y z 表示,点 x 和点y之间有一条边,边的权值为z。
Output包含一行,仅一个数,表示严格次小生成树的边权和。(数据保证必定存在严格次小生成树)
Sample Input5 6 1 2 1 1 3 2 2 4 3 3 5 4 3 4 3 4 5 6Sample Output11Hint
数据中无向图无自环; 50% 的数据N≤2 000 M≤3 000; 80% 的数据N≤50 000 M≤100 000; 100% 的数据N≤100 000 M≤300 000 ,边权值非负且不超过 10^9 。
扪心自问,kruskal的本质是什么?
贪心
kruskal算法被证明,对于任何的u,v
有u到v之间边权最大值小于等于u到v未选入的边的边权
所以说,不严格次小生成树只要
遍历每条未选的边(u,v,d),用它替换u和v之间的最大边即可
现在我们的任务就是把不严格的不去掉
为什么它不严格?
因为
u到v之间边权最大值小于等于u到v未选入的边的边权
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; #define ll long long const int M = 100000 + 5; const int ME = 6e5 + 10, P = 18; long long inf = 1e14; ll mx[M][P+1], mi[M][P+1]; int dep[M], anc[M][P+1], fa[M], tot, h[M]; bool ban[ME], vis[M]; struct edge{int v, w, nxt;}G[M<<1]; struct node{int u, v, w, id;}g[ME]; void add(int u, int v, int w){ G[++tot].v =v, G[tot].nxt = h[u], G[tot].w = w, h[u] = tot; } bool cmp(node A, node B){ return A.w < B.w; } int find(int u){return u == fa[u] ? u : fa[u] = find(fa[u]);} void dfs(int u, int f, int w){ vis[u] = 1; dep[u] = dep[f] + 1; anc[u][0] = f, mx[u][0] = w, mi[u][0] = -inf; for(int p = 1; p <= P; p++){ anc[u][p] = anc[anc[u][p-1]][p-1]; mx[u][p] = max(mx[u][p-1], mx[anc[u][p-1]][p-1]); mi[u][p] = max(mi[u][p-1], mi[anc[u][p-1]][p-1]); if(mx[u][p-1] > mx[anc[u][p-1]][p-1]) mi[u][p] = max(mi[u][p], mx[anc[u][p-1]][p-1]); else if(mx[u][p-1] < mx[anc[u][p-1]][p-1]) mi[u][p] = max(mi[u][p], mx[u][p-1]); } for(int i = h[u]; i; i = G[i].nxt){ int v = G[i].v; if(v == f)continue; dfs(v, u, G[i].w); } } int getlca(int u, int v){ if(dep[u] < dep[v]) swap(u, v); int t = dep[u] - dep[v]; for(int p = 0; t; t>>=1, p++) if(t&1) u = anc[u][p]; if(u == v) return u; for(int p = P; p >= 0; p--) if(anc[u][p] != anc[v][p]) u = anc[u][p], v = anc[v][p]; return anc[u][0]; } ll get(int u, int v, int w){ ll ret = -inf; for(int p = P; p >= 0; p--) if(dep[anc[u][p]] >= dep[v]){ if(w == mx[u][p]) ret = max(ret, mi[u][p]); else ret = max(ret, mx[u][p]); u = anc[u][p]; } return ret; } int main(){ long long ans = 0, ret = inf; int n, m, u, v, w; scanf("%d%d", &n,&m); for(int i = 1; i <= m; i++){ scanf("%d%d%d", &u, &v, &w); g[i] = (node){u, v, w, i}; } memset(mx[1], 0x8f, sizeof(mx[1])); memset(mi[1], 0x8f, sizeof(mi[1])); sort(g+1, g+1+m, cmp); for(int i = 1; i <= n; i++) fa[i] = i; for(int i = 1; i <= m; i++){ int u = g[i].u, v = g[i].v; if(find(u) == find(v)) continue; fa[find(u)] = find(v); ans += 1LL*g[i].w; add(u, v, g[i].w), add(v, u, g[i].w); ban[i] = 1; } dfs(1, 0, 0); for(int i = 1; i <= n; i++) if(!vis[i])return !puts("orz"); for(int i = 1; i <= m; i++){ if(ban[i])continue; int u = g[i].u, v = g[i].v; int lca = getlca(u, v); ll s = get(u, lca, g[i].w); ll p = get(v, lca, g[i].w); ret = min(ret, 1LL*g[i].w - max(s, p)); } printf("%lld\n", ans+ret); }