[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\n", L);
return 0;
}
TODO: “前缀交”版本