link
求操作後的所有路徑中邊權和最長的邊的最小值,二分
考慮 對答案的最短時間二分,每次得到一個二分值 mid
顯然,我們主要關心 路徑和比 mid 大的運輸路徑,這裡要維護樹上兩點間的路徑,所以要用到 lca,同時可以記錄每個點的距離(1 -> x)\(dist[x]\),這樣可以用邊字首和直接得到路徑 x -> y 和:
\[res = dist[x] + dist[y] - 2* dist[~lca(x, y)~]
\]
操作只能做一次,貪心地考慮肯定是要刪掉 任意一個經過所有 \(res > mid\) 的運輸路徑的邊的集合中邊權 \(\geq \max\{res_i - mid\}\) 的邊
也就是個判定性的問題
-
一定要經過所有運輸路徑,如果刪去的不是經過所有的,則肯定還有運輸路徑的時間要 > mid
-
刪去邊的邊權大於等於最大差值即可,剩下的顯然都會被覆蓋,也就是不會超過 mid
怎麼知道這個邊經過了幾條路徑呢?
那麼要對路徑上所有的邊 +1,記錄覆蓋,很容易想到用 樹上差分 維護即可。
時間複雜度為 \(O(n\log (n\cdot t_i))\)
做這題還有一個小心得(也不算吧,
在實現樹上差分的還原時,可以選擇再 dfs 一遍,也可以在 lca 初始化樹的時候開一個陣列記錄搜尋序,後邊倒序迴圈一遍求字首和。
我覺得以後還是用 後者更好,因為我老是把兩次的 dfs 函式名打混,時不時就調 n 年 ...
#include <bits/stdc++.h>
#define re register int
#define max(x, y) (x > y ? x : y)
using namespace std;
const int N = 3e5 + 10, logN = 50;
struct edge
{
int to, w, next;
}e[N << 1];
struct road
{
int x, y, p;
}a[N];
int top, h[N];
int dep[N], dist[N], f[N][logN], d[N];
int n, m;
int seq[N], idx;
inline void add(int x, int y, int w)
{
e[++ top] = (edge){y, w, h[x]};
h[x] = top;
}
void dfs(int u, int fa)
{
dep[u] = dep[fa] + 1;
seq[++ idx] = u;
f[u][0] = fa;
for (re i = 1; i <= log2(n); i ++)
f[u][i] = f[f[u][i - 1]][i - 1];
for (re i = h[u]; i; i = e[i].next)
{
int v = e[i].to, w = e[i].w;
if (v == fa) continue;
dist[v] = dist[u] + w;
dfs(v, u);
}
}
inline int lca(int u, int v)
{
if (dep[u] < dep[v]) swap(u, v);
for (re i= log2(n); i >= 0; i --)
if (dep[f[u][i]] >= dep[v]) u = f[u][i];
if (u == v) return u;
for (re i = log2(n); i >= 0; i --)
if (f[u][i] != f[v][i]) u = f[u][i], v = f[v][i];
return f[u][0];
}
/*
void cale(int u, int fa)
{
for (re i = h[u]; i; i = e[i].next)
{
int v = e[i].to;
if (v == fa) continue;
cale(v, u);
d[u] += d[v];
}
}
*/
inline bool check(int mid)
{
memset(d, 0, sizeof(d));
int cnt = 0, maxt = 0;
for (re i = 1; i <= m; i ++)
{
int x = a[i].x, y = a[i].y, p = a[i].p;
int t = dist[x] + dist[y] - dist[p] * 2;
if (t > mid)
{
d[x] += 1;
d[y] += 1;
d[p] -= 2;
cnt ++;
maxt = max(maxt, t - mid);
}
}
if (!cnt) return true;
// cale(1, 0);
for (re i = n; i >= 1; i --)
{
int x = seq[i];
d[f[x][0]] += d[x];
}
for (re i = 2; i <= n; i ++)
if (d[i] == cnt && dist[i] - dist[f[i][0]] >= maxt) return true;
return false;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin >> n >> m;
for (re i = 1; i < n; i ++)
{
int x, y, w; cin >> x >> y >> w;
add(x, y, w); add(y, x, w);
}
dfs(1, 0);
for (re i = 1; i <= m; i ++)
{
int x, y; cin >> x >> y;
a[i] = (road){x, y, lca(x, y)};
}
int l = 0, r = 3e8;
while (l < r)
{
int mid = (l + r) / 2;
if (check(mid)) r = mid;
else l = mid + 1;
}
cout << l << '\n';
return 0;
}