首先两边dfs找出树的直径,并记录直径路径,因为题目要找最小偏心距,根据定义,直径上的点偏心距是最小的,但要求选取一段不超过s的距离求最小偏心距,题目数据虽然较小,但也要想个好方法以较小的时间复杂度求出答案,因为直径一定是最长的路径,所以我们第二遍dfs的时候相当于求出了每个点到直径一个端点的距离,因此我们可以联想到 对于一条直径上的点来说,到某点最远的距离要么是树的直径的一端要么是树的直径的另一端,所以可以双指针固定一段长度不超过s的区间,然后记录这段区间到直径两个端点的最小值,然后就可以把直径的点打上标记,再dfs求出不经过直径上的点所能到达的最远距离,取最大的就是最大偏心距,也就是说 最大偏心距 要么是由直径上选取区间的两端点到直径的一端的距离,要么是直径上的点在不经过直径上的点的情况下能到达的最远距离,时间复杂度O(n)
AC代码:
#include <bits/stdc++.h> using namespace std; using LL = long long; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n, s; cin >> n >> s; vector<vector<pair<int, int>>> G(n + 1); for (int i = 1; i <= n - 1; i++) { int u, v, w; cin >> u >> v >> w; G[u].push_back(make_pair(v, w)); G[v].push_back(make_pair(u, w)); } int l = 0, r = 0; vector<int> dis(n + 1), f(n + 1); int root = 0; iota(f.begin(), f.end(), 0); function<void(int, int)> dfs = [&](int u, int fa) { f[u] = fa; if (dis[u] > dis[root]) { root = u; } for (auto v : G[u]) { if (v.first != fa) { dis[v.first] = dis[u] + v.second; dfs(v.first, u); } } }; dfs(1, 0); l = root; dis[root] = 0; dfs(root, 0); r = root; int ans = 0x3f3f3f3f; for (int i = r, j = r; i; i = f[i]) { while (dis[j] - dis[i] > s) { j = f[j]; } int x = max(dis[r] - dis[j], dis[i]); ans = min(ans, x); } vector<int> vis(n + 1); for (int i = r; i; i = f[i]) { vis[i] = 1; } function<void(int, int)> dfs2 = [&](int u, int fa) { for (auto v : G[u]) { if (!vis[v.first] and v.first != fa) { dis[v.first] = dis[u] + v.second; dfs2(v.first, u); } } }; for (int i = r; i; i = f[i]) { dis[i] = 0; dfs2(i, f[i]); } for (int i = 1; i <= n; i++) { ans = max(ans, dis[i]); } cout << ans << '\n'; return 0; }
P1099 [NOIP2007 提高组] 树网的核(树的直径)
最新推荐文章于 2024-05-20 20:08:58 发布