牛客小白月赛89

题目链接


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;
}
posted @ 2024-04-09 18:31  xqy2003  阅读(1)  评论(0编辑  收藏  举报