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;
}
posted @ 2023-06-15 10:36  5k_sync_closer  阅读(1)  评论(0编辑  收藏  举报  来源