首先存下每个运输计划的起点
st[i]
,终点
ed[i]
,最近公共祖先
lca[i]
以及耗时
len[i]
。
先二分最短时间
time
。
在判定中,先标记出所有
len[i]>time
的运输计划,虫洞必须位于这些运输计划的路径的公共边上。
这里,为每个点赋予一个权值,初始为
0
。
然后对于任意一个
这样容易知道,最后如果一个点的点权等于所有
对于路径加
1
的操作,可以使用树剖实现,但是常数较大。但是可以发现这里的询问都是单点询问,而且都在路径修改之后。对于这一点,可以使用差分数组代替树剖,将复杂度去掉一个
建立差分数组
b[u]=val[u]−∑val[v]
,其中
val[u]
为
u
的点权,
对于
而单点询问就是求子树和。最后
DFS
一遍就可以求出所有点的点权。
代码:
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
inline int read() {
int res = 0; bool bo = 0; char c;
while (((c = getchar()) < '0' || c > '9') && c != '-');
if (c == '-') bo = 1; else res = c - 48;
while ((c = getchar()) >= '0' && c <= '9')
res = (res << 3) + (res << 1) + (c - 48);
return bo ? ~res + 1 : res;
}
const int N = 3e5 + 5, LogN = 23;
int n, m, a[N], ecnt, nxt[N << 1], adj[N], go[N << 1], fa[N][LogN],
dep[N], T[N], val[N << 1], dis[N], len[N], st[N], ed[N],
maxv, _lca[N], top[N], v_res, cnt_now;
void add_edge(int u, int v, int w) {
nxt[++ecnt] = adj[u]; adj[u] = ecnt;
go[ecnt] = v; val[ecnt] = w;
}
void dfs(int u, int fu) {
int i; dep[u] = dep[fu] + 1; for (i = 1; i <= 21; i++)
fa[u][i] = fa[fa[u][i - 1]][i - 1];
for (int e = adj[u], v; e; e = nxt[e]) {
if ((v = go[e]) == fu) continue;
fa[v][0] = u; dis[v] = dis[u] + val[e];
top[v] = val[e]; dfs(v, u);
}
}
int lca(int u, int v) {
int i; if (dep[u] < dep[v]) swap(u, v);
for (i = 21; i >= 0; i--) {
if (dep[fa[u][i]] >= dep[v]) u = fa[u][i];
if (u == v) return u;
}
for (i = 21; i >= 0; i--) if (fa[u][i] != fa[v][i])
u = fa[u][i], v = fa[v][i];
return fa[u][0];
}
int dist(int i, int u, int v) {
return dis[u] + dis[v] - (dis[_lca[i] = lca(u, v)] << 1);
}
void change(int i, int x) {
T[st[i]] += x; T[ed[i]] += x;
T[_lca[i]] -= x << 1;
}
int dfs_ans(int u, int fu) {
int res_d = T[u];
for (int e = adj[u], v; e; e = nxt[e]) {
if ((v = go[e]) == fu) continue;
res_d += dfs_ans(v, u);
}
if (res_d == cnt_now) v_res = max(v_res, top[u]);
return res_d;
}
bool check(int mid) {
int i, res = 0; for (i = 0; i <= n; i++) T[i] = 0; cnt_now = 0;
for (i = 1; i <= m; i++) if (len[i] > mid) cnt_now++, change(i, 1);
if (!cnt_now) return 1; v_res = 0; dfs_ans(1, 0);
return maxv - v_res <= mid;
}
int solve() {
int l = 0, r = maxv, mid;
while (l <= r) {
mid = l + r >> 1;
if (check(mid)) r = mid - 1;
else l = mid + 1;
}
return l;
}
int main() {
int i, x, y, z; n = read(); m = read();
for (i = 1; i < n; i++) {
x = read(); y = read(); z = read();
add_edge(x, y, z); add_edge(y, x, z);
}
dfs(1, 0);
for (i = 1; i <= m; i++) {
st[i] = read(); ed[i] = read();
len[i] = dist(i, st[i], ed[i]);
maxv = max(maxv, len[i]);
}
printf("%d\n", solve());
return 0;
}