Description
給出一棵無權樹(可理解為邊權為 \(1\))。
你需要選取三個點 \(a,b,c\),最大化 \(a,b\) 和 \(b,c\) 和 \(a,c\) 的簡單路徑的並集的長度。
輸出這個最大長度和 \(a,b,c\)。
Solution
有一個結論:
必定會存在一組最優解,使得 \(a,b\) 是樹直徑上的端點。
那我們可以套路地去把樹直徑兩端點求出來,推薦大家用兩次搜索求出樹直徑端點。
確定了 \(a,b\),接下來我們只要去找到最優的 \(c\),就可以最大化答案了。
此時我們注意到:\(a,b\) 和 \(b,c\) 和 \(a,c\) 的簡單路徑的並集的長度其實就是 \(\frac{\text{dist}(a,b)+\text{dist}(b,c)+\text{dist}(a,c)}{2}\)。
此時 \(\text{dist}(a,b)\) 已經確定了,當 \(\text{dist}(b,c)+\text{dist}(a,c)\) 的值取到最大,那么整個式子取最大。
把 \(a,b\) 到所有點的簡單路徑距離求出來,去枚舉這個最優的 \(c\) 即可,枚舉的過程中記得判與 \(a,b\) 相同的情況。
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#define RI register int
using namespace std;
inline int read() {
int x = 0, f = 1;
char s = getchar();
while (s < '0' || s > '9') {
if (s == '-')
f = -f;
s = getchar();
}
while (s >= '0' && s <= '9') {
x = x * 10 + s - '0';
s = getchar();
}
return x * f;
}
const int N = 200100, M = 400100;
int n;
int tot, head[N], ver[M], edge[M], Next[M];
void add(int u, int v, int w) {
ver[++tot] = v;
edge[tot] = w;
Next[tot] = head[u];
head[u] = tot;
}
int d[N], vis[N];
int pos;
void bfs(int sta) {
memset(d, 0, sizeof(d));
memset(vis, 0, sizeof(vis));
queue<int>q;
q.push(sta);
vis[sta] = 1;
while (q.size()) {
int u = q.front();
q.pop();
for (RI i = head[u]; i; i = Next[i]) {
int v = ver[i], w = edge[i];
if (vis[v])
continue;
d[v] = d[u] + w;
vis[v] = 1;
if (d[v] > d[pos])
pos = v;
q.push(v);
}
}
}
int p1, p2;
int ans;
int tmp1[N], tmp2[N];
int main() {
n = read();
for (RI i = 1; i < n; i++) {
int u = read(), v = read();
add(u, v, 1), add(v, u, 1);
}
bfs(1);
p1 = pos;
bfs(p1);
p2 = pos;
for (RI i = 1; i <= n; i++)
tmp1[i] = d[i];
bfs(p2);
for (RI i = 1; i <= n; i++)
tmp2[i] = d[i];
pos = 0;
for (RI i = 1; i <= n; i++)
if (tmp1[i] + tmp2[i] > tmp1[pos] + tmp2[pos] && i != p1 && i != p2)
pos = i;
ans = (tmp1[p2] + tmp1[pos] + tmp2[pos]) / 2;
printf("%d\n", ans);
printf("%d %d %d\n", p1, p2, pos);
return 0;
}