題目連結
F
賽後補題,覺得值得注意的性質就是, \(LCA\) 是樹上列舉計數要去考慮的方法。(例如點分治)
假設答案的兩點 \(x\) , \(y\) , 那麼 \(ans = max \left( 2ti[x] , 2ti[y] , ti[x] + d[x \rightarrow lca] + d[y \rightarrow lca]\right)\).
這樣我們以 \(lca\) (子樹根 \(u\)) 去維護 \(ti[x] + d[x \rightarrow u]\) 的最大值與次大值即可。
但注意有 \(ti[x]\) 與 \(ti[y]\) 相差過大的情況,這樣答案就為 \(max\left(2ti[x] , 2ti[y]) \right)\)
這樣答案就有了二分性,二分答案 \(md\) , 只有 \(2ti[x] <= md\) 才入選 \(dp\) 更新,
如果此時更新出的 \(dp\) 答案小於 \(md\) , 答案就能更小。
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
void solve() {
int n,m;
cin >> n >> m;
vector<vector<int>> g(n + 1);
for (int i = 1 ; i <= n - 1 ; ++i) {
int u,v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
int ans = 2E9;
vector<int> a(m + 1),t(m + 1);
vector<vector<int>> e(n + 1);
for (int i = 1 ; i <= m ; ++i) {
cin >> a[i] >> t[i];
e[a[i]].push_back(t[i]);
}
auto check = [&] (int md) {
vector<int> dp1(n + 1 , 1E9),dp2(n + 1 , 1E9);
auto update = [&] (int u,int w) {
if (dp1[u] > w) {
dp2[u] = dp1[u];
dp1[u] = w;
} else if (dp2[u] > w) {
dp2[u] = w;
}
};
function<void(int,int)> dfs = [&] (int u,int par) {
for (auto v : g[u]) {
if (v == par) continue;
dfs(v , u);
update(u , dp1[v] + 1);
update(u , dp2[v] + 1);
}
for (auto x : e[u]) {
if (2 * x <= md) update(u , x);
}
};
dfs(1 , 0);
int mn = 2E9;
for (int i = 1 ; i <= n ; ++i) {
mn = min(mn , dp1[i] + dp2[i]);
}
return mn;
};
int l = 0 , r = 3E6;
while (l < r) {
int md = l + r >> 1;
if (check(md) <= md) r = md;
else l = md + 1;
}
cout << r << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T = 1;
while (T--) {
solve();
}
return 0;
}