[NOIp2015]运输计划

Description

BZOJ4326
Luogu2680
给定一棵树,和若干路径,求使一条边长度变为0后,最长路径的最小值。

Solution

链上的情况可以求出路径的交(差分或者运用前缀和的思想求“前缀交”),然后二分答案统计即可。
推广到树上,首先可以树上差分,然后二分答案统计,也可以尝试树上路径求“前缀交”。

Code

差分版:

#include <cstdio>
#include <algorithm>
#include <cstring>

const int N = 3e5 + 10;
const int M = 2*N + 10;

int hd[N], to[M], nxt[M], w[M], cnt;
int dep[N], fa[N], sz[N], son[N];
int top[N], id[N], rnk[N], tot;
int n, m, mxd, mxe, fck;
int flag[N], dis[N];
int px[N], py[N], plca[N], pd[N];

void adde(int x, int y, int z) {
    cnt++;
    to[cnt] = y; nxt[cnt] = hd[x]; w[cnt] = z;
    hd[x] = cnt;
}

int dfs1(int x, int f) {
    fa[x] = f;
    dep[x] = dep[f] + 1;
    sz[x] = 1;
    for (int i = hd[x]; i; i = nxt[i]) if (to[i] != f) {
        dis[to[i]] = dis[x] + w[i];
        sz[x] += dfs1(to[i], x);
        if (sz[to[i]] > sz[son[x]])
            son[x] = to[i];
    }
    return sz[x];
}

void dfs2(int x, int f) {
    top[x] = f;
    id[x] = tot++;
    rnk[tot] = x;
    if (!son[x]) return;
    dfs2(son[x], f);
    for (int i = hd[x]; i; i = nxt[i]) if (to[i] != fa[x] && to[i] != son[x]) {
        dfs2(to[i], to[i]);
    }
}

int lca(int x, int y) {
    while (top[x] != top[y]) {
        if (dep[top[x]] > dep[top[y]]) x = fa[top[x]];
        else y = fa[top[y]];
    }
    return dep[x] > dep[y] ? y : x;
}

bool check(int x) {
    fck = 0; memset(flag, 0, sizeof flag);
    for (int i = 1; i <= m; ++i) if (pd[i] > x) {
        flag[px[i]]++;
        flag[py[i]]++;
        flag[plca[i]] -= 2;
        fck++;
    }
    for (int i = n; i; --i) {
        int &j = rnk[i];
        flag[fa[j]] += flag[j];
        if (flag[j] == fck) {
            int fe = dis[j] - dis[fa[j]];
        	if (mxd - fe <= x) return true;
        }
    }
    return false;
}

int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1, x, y, z; i < n; ++i) {
        scanf("%d%d%d", &x, &y, &z);
        adde(x, y, z);
        adde(y, x, z);
        mxe = std::max(mxe, z);
    }
    dfs1(1, 0);
    dfs2(1, 1);
    for (int i = 1; i <= m; ++i) {
        scanf("%d%d", &px[i], &py[i]);
        plca[i] = lca(px[i], py[i]); 
        pd[i] = dis[px[i]] + dis[py[i]] - 2 * dis[plca[i]];
        mxd = std::max(mxd, pd[i]);
    }
    int L = mxd - mxe, R = mxd;
    while (L < R) {
        int mid = (L + R) >> 1;
        if (check(mid)) {
            R = mid;
        } else {
            L = mid+1;
        }
    }
    printf("%d
", L);
    return 0;
}

TODO: “前缀交”版本

原文地址:https://www.cnblogs.com/wyxwyx/p/noip201523.html