CF827D Best Edge Weight 题解
树上倍增+可并堆。
把原图的 MST 建出来,分别考虑非树边和树边的答案。
对于非树边 $(u,v)$,其最大为 $u\to v$ 路径最大值 $-1$ 时,可以替换掉这个最大值,加入 MST。
树上倍增维护路径最大值即可。
对于树边 $(u,v)$,其最大为所有覆盖其的非树边的最小值 $-1$ 时,不会被这些非树边替换,在 MST 中。
即对每条非树边在 MST 上的路径,对这条非树边的权值 check min。
用可并堆维护 check min 操作。
具体地,对每条非树边 $(u,v,w)$,在堆 $q_u$ 和 $q_v$ 中加入 $(w,\operatorname{lca}(u,v))$,
从下往上合并每个点的堆,合并到 $u$ 点时 $q_u$ 包含所有端点在 $u$ 子树内的 check min 操作。
这些操作中,只有 $\operatorname{lca}$ 的深度小于 $u$ 的操作可以影响到 $(u,fa_u)$ 这条边,所以把堆顶上 $\operatorname{lca}$ 深度大于等于 $u$ 的操作弹出,
此时的堆顶即为 $(u,fa_u)$ 这条树边的答案。特别地,堆为空时 $(u,fa_u)$ 的答案为 $-1$。
#include <cstdio>
#include <algorithm>
#include <ext/pb_ds/priority_queue.hpp>
#include <ext/pb_ds/assoc_container.hpp>
using namespace std;
struct E
{
int v, w, t;
} e[400050];
struct S
{
int u, v, w, i;
} g[200050];
__gnu_pbds::priority_queue<pair<int, int>, greater<pair<int, int>>> q[200050];
int n, m, c, z[200050], r[200050], d[200050], h[200050], f[20][200050], w[20][200050];
bool o[200050];
bool C(S a, S b) { return a.w < b.w; }
bool R(S a, S b) { return a.i < b.i; }
int F(int x) { return x == z[x] ? x : z[x] = F(z[x]); }
void A(int u, int v, int w) { e[++c] = {v, w, h[u]}, h[u] = c; }
void D(int u)
{
for (int i = h[u], v; i; i = e[i].t)
if (!d[v = e[i].v])
{
w[0][v] = e[i].w;
d[v] = d[f[0][v] = u] + 1;
for (int j = 1; f[j - 1][v]; ++j)
f[j][v] = f[j - 1][f[j - 1][v]], w[j][v] = max(w[j - 1][v], w[j - 1][f[j - 1][v]]);
D(v);
}
}
int L(int u, int v)
{
if (d[u] < d[v])
swap(u, v);
while (d[u] > d[v])
u = f[__lg(d[u] - d[v])][u];
if (u == v)
return u;
for (int k = __lg(d[u]); k >= 0; --k)
if (f[k][u] != f[k][v])
u = f[k][u], v = f[k][v];
return f[0][u];
}
int Q(int u, int v)
{
int q = 0;
if (d[u] < d[v])
swap(u, v);
while (d[u] > d[v])
q = max(q, w[__lg(d[u] - d[v])][u]), u = f[__lg(d[u] - d[v])][u];
if (u == v)
return q;
for (int k = __lg(d[u]); k >= 0; --k)
if (f[k][u] != f[k][v])
q = max({q, w[k][u], w[k][v]}), u = f[k][u], v = f[k][v];
return max({q, w[0][u], w[0][v]});
}
void M(int u, int k)
{
for (int i = h[u], v; i; i = e[i].t)
if ((v = e[i].v) != k)
M(v, u), q[u].join(q[v]);
while (!q[u].empty() && d[q[u].top().second] >= d[u])
q[u].pop();
if (!q[u].empty())
r[u] = q[u].top().first - 1;
else
r[u] = -1;
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i)
z[i] = i;
for (int i = 0; i < m; ++i)
scanf("%d%d%d", &g[i].u, &g[i].v, &g[i].w), g[i].i = i;
sort(g, g + m, C);
for (int i = 0, u, v, p = 0; i < m; ++i)
if ((u = F(g[i].u)) != (v = F(g[i].v)))
{
z[u] = v;
o[g[i].i] = 1;
A(g[i].u, g[i].v, g[i].w);
A(g[i].v, g[i].u, g[i].w);
if (++p == n - 1)
break;
}
D(d[1] = 1);
for (int i = 0, l; i < m; ++i)
if (!o[g[i].i])
q[g[i].u].push({g[i].w, l = L(g[i].u, g[i].v)}), q[g[i].v].push({g[i].w, l});
M(1, 0);
sort(g, g + m, R);
for (int i = 0; i < m; ++i)
if (o[i])
printf("%d ", r[d[g[i].u] > d[g[i].v] ? g[i].u : g[i].v]);
else
printf("%d ", Q(g[i].u, g[i].v) - 1);
return 0;
}