牛客小白月賽89

xqy2003發表於2024-04-09

題目連結


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;
}