严格次小生成树
前言
变态题。调了三四天才调出来。
如果最小生成树选择的边集是 \(E_M\),严格次小生成树选择的边集是 \(E_S\),那么需要满足:(\(\operatorname{value}(e)\) 表示边 \(e\) 的权值)\(\sum_{e\in E_M}\operatorname{value}(e)<\sum_{e\in E_S}\operatorname{value}(e)\)。
注意:是严格小于!!!
大致思路如下:
- 先跑最小生成树(以 kruskal 为例),标记每一条加入最小生成树的边,算出最小生成树的边权和 \(sum\)。
- 遍历所有不在最小生成树中的边 \(e\),将其加入。这时会出现一个环,找到环上的最大值 \(maxx\),则必有 \(maxx\le \operatorname{value}(e)\),故我们只需把 \(maxx\) 所对应的边扔掉,加入 \(e\),得到 \(res=sum-maxx+\operatorname{value}(e)\)。
- \(ans=\max(res)\)。
但有一个问题。
上面说了 \(maxx\le \operatorname{value}(e)\)。
\(maxx=\operatorname{value}(e)\)!!!
这样就不严格了。
为了防止相等,我们需要多存一个次大值 \(minn\)(不是最小值)
找环上最大值可用 LCA(倍增),设要加入的边两端分别是 \(u\) 和 \(v\),我们求出 \(lcaa=\operatorname{LCA}(u,v)\),然后查询 \(u\to lcaa\) 最大值和 \(v\to lcaa\) 最大值,两者取 \(\max\) 即可。查询思路类似 LCA 中让 \(x\) 和 \(y\) 深度相等的部分,边跳边找。
int query(int u, int v, int w)
{
int res = -INF;
while (dep[u] > dep[v])
{
int h = lg[dep[u] - dep[v]];
if (w != maxx[u][h])
{
res = max(res, maxx[u][h]);
}
else
{
res = max(res, minn[u][h]);
}
u = fa[u][h];
}
return res;
}
关键在于如何更新 \(maxx\) 和 \(minn\)。
\(\colorbox{lightgrey}{A}\;\text{---------}\;\colorbox{lightgrey}{B}\;\text{---------}\;\colorbox{lightgrey}{C}\)
这里 \(B\) 相当于 \(A\) 的 \(2^{i-1}\) 级祖先;\(C\) 相当于 \(B\) 的 \(2^{i-1}\) 级祖先,即 \(A\) 的 \(2^i\) 级祖先。
显然 \(maxx(A)=\max(maxx(B),maxx(C))\)
对于 \(minn\),分情况讨论:
- \(maxx(B)=maxx(C)\):此时 \(minn(A)=\max(minn(B),minn(C))\)
- \(maxx(B)<maxx(C)\):\(minn(B)\) 肯定没希望了,\(minn(A)=\max(maxx(B),minn(C))\)
- \(maxx(B)>maxx(C)\):同理 \(minn(A)=\max(minn(B),maxx(C))\)
void work()
{
for (int j = 1; j <= 18; j++)
{
for (int i = 1; i <= n; i++)
{
fa[i][j] = fa[fa[i][j - 1]][j - 1];
maxx[i][j] = max(maxx[i][j - 1], maxx[fa[i][j - 1]][j - 1]);
if (maxx[i][j - 1] == maxx[fa[i][j - 1]][j - 1])
{
minn[i][j] = max(minn[i][j - 1], minn[fa[i][j - 1]][j - 1]);
}
else if (maxx[i][j - 1] < maxx[fa[i][j - 1]][j - 1])
{
minn[i][j] = max(maxx[i][j - 1], minn[fa[i][j - 1]][j - 1]);
}
else
{
minn[i][j] = max(minn[i][j - 1], maxx[fa[i][j - 1]][j - 1]);
}
}
}
}
Code
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#define int long long
using namespace std;
const int MAXN = 1e5 + 5;
const int MAXM = 3e5 + 5;
const int MAXLOG = 20;
const int INF = 0x3f3f3f3f3f3f3f3fll;
int n, m, sum, ans = INF, max1, max2, cnt;
int head[MAXN], f[MAXN], fa[MAXN][MAXLOG], dep[MAXN], lg[MAXN], maxx[MAXN][MAXLOG], minn[MAXN][MAXLOG];
struct edge1
{
int from, to, dis;
bool vis;
bool operator <(const edge1 &x)const
{
return x.dis > dis;
}
}e1[MAXM];
struct edge2
{
int to, dis, nxt;
}e2[MAXM << 1];
void add(int u, int v, int w)
{
e2[++cnt] = edge2{v, w, head[u]};
head[u] = cnt;
}
void init()
{
for (int i = 1; i <= n; i++)
{
f[i] = i;
}
}
int find(int x)
{
if (x == f[x])
{
return x;
}
else
{
return f[x] = find(f[x]);
}
}
void kruskal()
{
sort(e1 + 1, e1 + m + 1);
init();
int tot = 0;
for (int i = 1; i <= m; i++)
{
int u = e1[i].from, v = e1[i].to, w = e1[i].dis;
int fu = find(u), fv = find(v);
if (fu != fv)
{
f[fu] = fv;
sum += w;
e1[i].vis = true;
add(u, v, w);
add(v, u, w);
if (++tot == n - 1)
{
break;
}
}
}
}
void dfs(int u, int father)
{
fa[u][0] = father;
dep[u] = dep[father] + 1;
minn[u][0] = -INF;
for (int i = head[u]; i; i = e2[i].nxt)
{
int v = e2[i].to;
if (v != father)
{
maxx[v][0] = e2[i].dis;
dfs(v, u);
}
}
}
int lca(int x, int y)
{
if (dep[x] < dep[y])
{
swap(x, y);
}
while (dep[x] > dep[y])
{
x = fa[x][lg[dep[x] - dep[y]]];
}
if (x == y)
{
return x;
}
for (int i = 18; i >= 0; i--)
{
if (fa[x][i] != fa[y][i])
{
x = fa[x][i], y = fa[y][i];
}
}
return fa[x][0];
}
void work()
{
for (int j = 1; j <= 18; j++)
{
for (int i = 1; i <= n; i++)
{
fa[i][j] = fa[fa[i][j - 1]][j - 1];
maxx[i][j] = max(maxx[i][j - 1], maxx[fa[i][j - 1]][j - 1]);
if (maxx[i][j - 1] == maxx[fa[i][j - 1]][j - 1])
{
minn[i][j] = max(minn[i][j - 1], minn[fa[i][j - 1]][j - 1]);
}
else if (maxx[i][j - 1] < maxx[fa[i][j - 1]][j - 1])
{
minn[i][j] = max(maxx[i][j - 1], minn[fa[i][j - 1]][j - 1]);
}
else
{
minn[i][j] = max(minn[i][j - 1], maxx[fa[i][j - 1]][j - 1]);
}
}
}
}
int query(int u, int v, int w)
{
int res = -INF;
while (dep[u] > dep[v])
{
int h = lg[dep[u] - dep[v]];
if (w != maxx[u][h])
{
res = max(res, maxx[u][h]);
}
else
{
res = max(res, minn[u][h]);
}
u = fa[u][h];
}
return res;
}
signed main()
{
scanf("%lld%lld", &n, &m);
for (int i = 1; i <= m; i++)
{
scanf("%lld%lld%lld", &e1[i].from, &e1[i].to, &e1[i].dis);
}
kruskal();
for (int i = 2; i <= n; i++)
{
lg[i] = lg[i >> 1] + 1;
}
dfs(1, 0);
work();
for (int i = 1; i <= m; i++)
{
if (!e1[i].vis)
{
int u = e1[i].from, v = e1[i].to, w = e1[i].dis, lcaa = lca(u, v);
int maxu = query(u, lcaa, w), maxv = query(v, lcaa, w);
ans = min(ans, sum - max(maxu, maxv) + w);
}
}
printf("%lld", ans);
return 0;
}