题意:
树上找两个点 使得其它点到这两点随意一点的距离的最大值最小
思路:
最大值最小 想到二分 在二分的基础上判定这个最大值是否可能
怎样判定这个问题就是怎样选那两个点的问题 非常明显 我们要处理的是直径(不然没意义 最长的就是直径) 那么既然已经有了一个要判定的值x 最好还是就选择距离直径两端点距离为x的点就好
直径上的点最多n个 算上二分的复杂度 O(nlogn)能够解决
代码:
#include<cstdio> #include<iostream> #include<cstring> #include<string> #include<algorithm> #include<map> #include<set> #include<vector> #include<queue> #include<cstdlib> #include<ctime> #include<cmath> using namespace std; typedef long long LL; #define N 200010 int t, n; struct edge { int v, next; } ed[N * 2]; int head[N], tot; int dis[N], pre[N]; int route[N], m; void add(int u, int v) { ed[tot].v = v; ed[tot].next = head[u]; head[u] = tot++; } int qu[N]; int bfs(int u) { int l = 0, r = 1; int pos = u, val = 0; dis[u] = 0; qu[0] = u; while (l < r) { u = qu[l++]; for (int i = head[u]; ~i; i = ed[i].next) { int v = ed[i].v; if (dis[v] == -1) { pre[v] = u; dis[v] = dis[u] + 1; qu[r++] = v; if (dis[v] > val) { val = dis[v]; pos = v; } } } } return pos; } int flag[N]; bool yes(int ans) { memset(flag, 0, sizeof(flag)); memset(dis, -1, sizeof(dis)); bfs(route[ans]); for (int i = 1; i <= n; i++) { if (dis[i] <= ans) flag[i] = 1; } memset(dis, -1, sizeof(dis)); bfs(route[m - 1 - ans]); for (int i = 1; i <= n; i++) { if (dis[i] <= ans) flag[i] = 1; } for (int i = 1; i <= n; i++) { if (!flag[i]) return false; } return true; } int main() { int i, u, v; scanf("%d", &t); while (t--) { tot = 0; memset(head, -1, sizeof(head)); scanf("%d", &n); for (i = 1; i < n; i++) { scanf("%d%d", &u, &v); add(u, v); add(v, u); } memset(dis, -1, sizeof(dis)); u = bfs(1); memset(dis, -1, sizeof(dis)); memset(pre, -1, sizeof(pre)); v = bfs(u); m = 0; while (v != -1) { route[m++] = v; v = pre[v]; } int l = 0, r = m - 1, mid, ans[3]; while (l <= r) { mid = (l + r) / 2; if (yes(mid)) { ans[0] = mid; ans[1] = route[mid]; ans[2] = route[m - 1 - mid]; if (ans[1] == ans[2]) { for (i = 0; i < m; i++) { if (route[i] != ans[1]) { ans[2] = route[i]; break; } } } r = mid - 1; } else l = mid + 1; } printf("%d %d %d ", ans[0], ans[1], ans[2]); } return 0; }