运输计划
题目大意:给出一颗有 n n n 个点的树,和走完某条边的耗时。现有 m m m 个从点 u u u 到点 v v v 的需求,且我们可以把某一条边的耗时变成 0 0 0 ,问最短的最长耗时是多少。
我们可以考虑二分答案,那问题就是我们怎样进行 c h e c k check check 。我们先把路径长度超过 m i d mid mid 的需求记录下来,然后我们就是要找到一条边,被所有这些超时的需求的路径经过,且这个边尽可能长。为什么要找到所有超时需求都经过的边呢?因为如果我们找到的边不是所有超时需求( > m i d > mid >mid 的路径)都经过的,那我把其变成 0 0 0 ,只是把部分超时的需求的时间减少了一些,另外一些没经过这条边的总时间根本没有变化,也就是必定依然会存在超时的路径。这个路径要尽量长就不必多解释了,那么 c h e c k check check 是否成功,就看耗时最长超时路径减去我们找出来的符合条件的最长边是不是 ≤ m i d ≤ mid ≤mid 了。那么这里找到所有超时路径都经过的边就靠树上差分解决,树上差分可以得知每条边被经过多少次,那么这个次数等于超时需求个数,就代表这条边被所有超时需求经过。
c o d e code code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 3e5 + 2;
struct Edge {
int v;
ll val;
};
struct k {
int u;
int v;
int l;
ll dis;
}q[N];
int n, m, flag, sum;
int father[N], depth[N], size[N], son[N], top[N], cnt, back[N], t[N];
int tmp[N];
ll Max, mid;
vector<Edge> g[N];
void dfs_1(int u, int d, int b, int fa, ll tm) {
depth[u] = d;
size[u] = 1;
father[u] = fa;
back[u] = b;
t[u] = tm;
for (int i = 0; i < (int)g[u].size(); ++i) {
int v = g[u][i].v;
if (v != fa) {
dfs_1(v, d + 1, g[u][i].val, u, tm + g[u][i].val);
size[u] += size[v];
if (size[v] > size[son[u]]) {
son[u] = v;
}
}
}
}
void dfs_2(int u, int t) {
top[u] = t;
if (!son[u]) return ;
dfs_2(son[u], t);
for (int i = 0; i < (int)g[u].size(); ++i) {
int v = g[u][i].v;
if (v != son[u] && v != father[u]) {
dfs_2(v, v);
}
}
}
void dfs_3(int u) {
for (int i = 0; i < (int)g[u].size(); ++i) {
int v = g[u][i].v;
if (v != father[u]) {
dfs_3(v);
tmp[u] += tmp[v];
}
}
if (tmp[u] == sum && Max - back[u] <= mid) {
flag = 1;
}
}
bool check() {
memset(tmp, 0, sizeof tmp);
Max = -1;
flag = 0;
sum = 0;
for (int i = 0; i < m; ++i) {
if (q[i].dis > mid) {
sum++;
Max = max(Max, q[i].dis);
tmp[q[i].u]++; tmp[q[i].v]++; tmp[q[i].l] -= 2;
}
}
if (Max == -1ll) return true;
dfs_3(1);
if (flag) return true;
return false;
}
int lca(int x, int y) {
while(top[x] ^ top[y]) {
if (depth[top[x]] < depth[top[y]]) swap(x, y);
x = father[top[x]];
}
return depth[x] < depth[y] ? x : y;
}
int main() {
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
scanf("%d%d", &n, &m);
for (int i = 0; i < n - 1; ++i) {
int u, v;
ll val;
scanf("%d%d%lld", &u, &v, &val);
g[u].push_back({v, val});
g[v].push_back({u, val});
}
dfs_1(1, 0, 0, 0, 0);
dfs_2(1, 1);
for (int i = 0; i < m; ++i) {
scanf("%d%d", &q[i].u, &q[i].v);
q[i].l = lca(q[i].u, q[i].v);
q[i].dis = t[q[i].u] + t[q[i].v] - 2 * t[q[i].l];
}
ll l = 0, r = 1e11;
while(l <= r) {
mid = l + r >> 1ll;
if (check()) r = mid - 1;
else l = mid + 1;
}
printf("%lld", r + 1);
}