乱搞?????
Source
https://www.lydsy.com/JudgeOnline/problem.php?id=1977
Solution
一个结论:最小生成树和次小生成树的差别只有一条边。换句话说,次小生成树是最小生成树删掉一条边再加上图中的另一条边后得到的。
证明:反证法。假设最小生成树中有两条边
e1,e2
e
1
,
e
2
,权值和为
S1
S
1
,将这两条边删掉后再加边
e3,e4
e
3
,
e
4
后得到次小生成树,权值和
S2
S
2
。
那么一定有
S1−e1−e2+e3+e4=S2
S
1
−
e
1
−
e
2
+
e
3
+
e
4
=
S
2
且
e3+e4>e1+e2
e
3
+
e
4
>
e
1
+
e
2
。
得出
e3>e1
e
3
>
e
1
或
e4>e2
e
4
>
e
2
。
因此
S1−e1+e3
S
1
−
e
1
+
e
3
和
S1−e2+e4
S
1
−
e
2
+
e
4
两者中一定存在一者大于
S1
S
1
且小于
S2
S
2
,因此次小生成树的权值和不是
S2
S
2
,与假设矛盾。得证。
有了结论,就可以得到一个算法:
枚举非树边
e
e
,表示将要加上的边。加上边 后,生成树上会出现一个环。因此要去掉的边一定是生成树上
u
u
到 的路径上的边之一(
u
u
和 为边
e
e
的两端点)。
由于要求严格次小,因此如果 到
v
v
的路径上的最大边权等于边 的边权,那么要删去的边就是
u
u
到 的路径上的次大边,否则要删去的是
u
u
到 的路径上的最大边。每次枚举完加入的边
e
e
并找出删掉的边后,就可以更新答案,得到的权值和最小的结果就是次小生成树的边权和。
于是问题转化为:多组询问,每次询问一条路径上的边权的最大值和次大值。
显然用树上倍增解决。
:
u
u
向上跳 步到达的节点。
转移:
g0[u][i] g 0 [ u ] [ i ] : u u 向上跳 步经过边的最大边权。
转移:
g1[u][i] g 1 [ u ] [ i ] : u u 向上跳 步经过边的次大边权。如果不存在则为 −∞ − ∞ 。
转移分三种情况:
当 g0[u][i]<g0[f[u][i]][i] g 0 [ u ] [ i ] < g 0 [ f [ u ] [ i ] ] [ i ] 时:
当 g0[u][i]>g0[f[u][i]][i] g 0 [ u ] [ i ] > g 0 [ f [ u ] [ i ] ] [ i ] 时:
当 g0[u][i]=g0[f[u][i]][i] g 0 [ u ] [ i ] = g 0 [ f [ u ] [ i ] ] [ i ] 时:
在倍增求 LCA 的过程中,按照同样的方法合并路径上的 g0 g 0 和 g1 g 1 ,就能求出路径上边权的最大值和次大值。
Code
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Rof(i, a, b) for (i = a; i >= b; i--)
#define Edge(u) for (int e = adj[u], v; e; e = nxt[e]) if ((v = go[e]) != fu)
using namespace std;
inline int read() {
int res = 0; bool bo = 0; char c;
while (((c = getchar()) < '0' || c > '9') && c != '-');
if (c == '-') bo = 1; else res = c - 48;
while ((c = getchar()) >= '0' && c <= '9')
res = (res << 3) + (res << 1) + (c - 48);
return bo ? ~res + 1 : res;
}
typedef long long ll;
const int N = 1e5 + 5, L = 2e5 + 5, M = 3e5 + 5, Z = 20, INF = 0x3f3f3f3f;
int n, m, ecnt, fa[N], nxt[L], adj[N], go[L], val[L], f[N][Z], g0[N][Z],
g1[N][Z], sg0, sg1, dep[N]; ll mst, ans = 1ll << 62; bool sel[M];
struct cyx {int x, y, z;} a[M];
inline bool comp(const cyx &a, const cyx &b) {return a.z < b.z;}
int cx(int x) {if (fa[x] != x) fa[x] = cx(fa[x]); return fa[x];}
bool zm(int x, int y) {
int ix = cx(x), iy = cx(y); if (ix != iy) return fa[iy] = ix, 1; return 0;
}
void add_edge(int u, int v, int w) {
nxt[++ecnt] = adj[u]; adj[u] = ecnt; go[ecnt] = v; val[ecnt] = w;
nxt[++ecnt] = adj[v]; adj[v] = ecnt; go[ecnt] = u; val[ecnt] = w;
}
void dfs(int u, int fu) {
int i; f[u][0] = fu; dep[u] = dep[fu] + 1; For (i, 0, 16) {
f[u][i + 1] = f[f[u][i]][i]; g0[u][i + 1] = max(g0[u][i], g0[f[u][i]][i]);
int x = g0[u][i], y = g0[f[u][i]][i];
if (x < y) g1[u][i + 1] = max(x, g1[f[u][i]][i]);
else if (x > y) g1[u][i + 1] = max(g1[u][i], y);
else g1[u][i + 1] = max(g1[u][i], g1[f[u][i]][i]);
}
Edge(u) g0[v][0] = val[e], g1[v][0] = -INF, dfs(v, u);
}
void orz(int u, int v) {
sg0 = sg1 = -INF; int i, x, y; if (dep[u] < dep[v]) swap(u, v);
Rof (i, 17, 0) {
if (dep[f[u][i]] >= dep[v]) {
x = sg0; y = g0[u][i]; if (x < y) sg1 = max(x, g1[u][i]);
else if (x > y) sg1 = max(sg1, y);
else sg1 = max(sg1, g1[u][i]); sg0 = max(sg0, g0[u][i]); u = f[u][i];
}
if (u == v) return;
}
Rof (i, 17, 0) if (f[u][i] != f[v][i]) {
x = sg0; y = g0[u][i]; if (x < y) sg1 = max(x, g1[u][i]);
else if (x > y) sg1 = max(sg1, y); else sg1 = max(sg1, g1[u][i]);
sg0 = max(sg0, g0[u][i]); u = f[u][i];
x = sg0; y = g0[v][i]; if (x < y) sg1 = max(x, g1[v][i]);
else if (x > y) sg1 = max(sg1, y); else sg1 = max(sg1, g1[v][i]);
sg0 = max(sg0, g0[v][i]); v = f[v][i];
}
x = sg0; y = g0[u][0]; if (x < y) sg1 = max(x, g1[u][0]);
else if (x > y) sg1 = max(sg1, y); else sg1 = max(sg1, g1[u][0]);
sg0 = max(sg0, g0[u][0]); u = f[u][0];
x = sg0; y = g0[v][0]; if (x < y) sg1 = max(x, g1[v][0]);
else if (x > y) sg1 = max(sg1, y); else sg1 = max(sg1, g1[v][0]);
sg0 = max(sg0, g0[v][0]); v = f[v][0];
}
int main() {
int i; n = read(); m = read(); For (i, 1, m)
a[i].x = read(), a[i].y = read(), a[i].z = read();
For (i, 1, n) fa[i] = i; sort(a + 1, a + m + 1, comp); For (i, 1, m)
if (zm(a[i].x, a[i].y)) mst += a[i].z,
add_edge(a[i].x, a[i].y, a[i].z), sel[i] = 1;
dfs(1, 0); For (i, 1, m) if (!sel[i])
orz(a[i].x, a[i].y), ans = min(ans, mst + a[i].z -
(a[i].z == sg0 ? sg1 : sg0)); cout << ans << endl; return 0;
}