P2680 NOIP2015 提高组 运输计划

发布时间 2023-04-19 13:42:48作者: dbxxx

P2680 NOIP2015 提高组 运输计划

最小化最长的路径,考虑二分答案。

问题转化成检验删去一条边的边权后,最长路径权值能否不超过 \(x\)

考虑没删边权时,原先那些不超过 \(x\) 的路径,删去边权后肯定不会影响,直接忽略。

考虑原先比 \(x\) 长的那些路径。我们期望删边权后这些路径全部变短到 \(x\) 以内,因此称之为关键路径。

很明显,我们必须找一条被所有关键路径经过的边。否则,至少有一条关键路径的权值不会缩减,修改是无效的。

然后又很明显,我们直接贪心地找被所有关键路径经过的,权值最大的边,删除权值是最优的。

树上差分做到 \(\Theta((n + m)\log \sum t)\)

/*
 * @Author: crab-in-the-northeast 
 * @Date: 2023-04-19 12:55:53 
 * @Last Modified by: crab-in-the-northeast
 * @Last Modified time: 2023-04-19 13:33:47
 */
#include <bits/stdc++.h>
inline int read() {
    int x = 0;
    bool f = true;
    char ch = getchar();
    for (; !isdigit(ch); ch = getchar())
        if (ch == '-')
            f = false;
    for (; isdigit(ch); ch = getchar())
        x = (x << 1) + (x << 3) + ch - '0';
    return f ? x : (~(x - 1));
}
inline bool gmx(int &a, int b) {
    return b > a ? a = b, true : false;
}

const int maxn = 300005;
const int maxm = 300005;
const int mlgn = 20;

int n, m;

typedef std :: pair <int, int> pii;
std :: vector <pii> G[maxn];

int lg[maxn], fa[maxn], prew[maxn], dep[maxn], dfn[maxn], dis[maxn], mi[mlgn][maxn], times = 0;

inline int depmin(int u, int v) {
    return dep[u] < dep[v] ? u : v;
}

void dfs(int u, int ls) {
    dfn[u] = ++times;
    dep[u] = dep[ls] + 1;
    fa[u] = mi[0][dfn[u]] = ls;
    for (pii e : G[u]) {
        int v = e.first, w = e.second;
        if (v == ls)
            continue;
        dis[v] = dis[u] + w;
        prew[v] = w;
        dfs(v, u);
    }
}

inline void getmi() {
    for (int j = 1; j <= lg[n]; ++j)
        for (int i = 1; i + (1 << j) - 1 <= n; ++i)
            mi[j][i] = depmin(mi[j - 1][i], mi[j - 1][i + (1 << (j - 1))]);
}

inline int lca(int u, int v) {
    if (u == v)
        return u;
    u = dfn[u];
    v = dfn[v];
    if (u > v)
        std :: swap(u, v);
    int s = lg[v - u++];
    return depmin(mi[s][u], mi[s][v - (1 << s) + 1]);
}

struct query {
    int s, t, L;
} q[maxn];

int cnt[maxn];

void dfs2(int u) {
    for (pii e : G[u]) {
        int v = e.first;
        if (v == fa[u])
            continue;
        dfs2(v);
        cnt[u] += cnt[v];
    }
}

inline bool check(int x) {
    int tot = 0;
    std :: memset(cnt, 0, sizeof(cnt));
    for (int i = 1; i <= m; ++i) {
        int s = q[i].s, t = q[i].t, L = q[i].L, p = lca(s, t);
        if (L <= x) {
            tot = i - 1;
            break;
        }
        ++cnt[s];
        ++cnt[t];
        cnt[p] -= 2;
    }

    dfs2(1);

    int maxw = 0;
    for (int u = 1; u <= n; ++u) {
        if (cnt[u] == tot)
            gmx(maxw, prew[u]);
    }

    return q[1].L - maxw <= x;
}

int main() {
    n = read(); m = read();
    for (int i = 2; i <= n; ++i)
        lg[i] = lg[i >> 1] + 1;
    for (int i = 1; i < n; ++i) {
        int u = read(), v = read(), w = read();
        G[u].push_back({v, w});
        G[v].push_back({u, w});
    }
    dfs(1, 0);
    getmi();

    for (int i = 1; i <= m; ++i) {
        int s = q[i].s = read(), t = q[i].t = read(), p = lca(s, t);
        q[i].L = dis[s] + dis[t] - dis[p] * 2;
    }

    std :: sort(q + 1, q + 1 + m, [](query a, query b) {
        return a.L > b.L;
    });

    int ans = 0;
    if (check(0)) {
        puts("0");
        return 0;
    }
    
    for (int i = (1 << 30); i; i >>= 1)
        if (!check(ans + i))
            ans += i;
    printf("%d\n", ans + 1);
    return 0;
}