给一棵最多2*10^5个结点的树,选择两个结点放置设备,要求所有结点其到最近设备的最远距离最小,求出这个最小距离。
最大值最小,首先想到二分。二分一个最大距离M,先以1号结点bfs出每个结点的深度。任选一个最大深度的结点,则离他距离M的父结点u上必须要放置一个设备。然后再以u进行bfs,同样的选择出第二个结点。再把选择的两个设备结点加入队列bfs,看是否能够遍历所有点,若能就满足。
zju上面做的,dfs会栈溢出。第一次以一号结点bfs可以预处理,之后直接取出最大深度的点就行。有可能两次找的放置设备的点相同,注意特判一下。二分过程完成后,有可能最后一次判断不成立,此时得到的两个设备的点不是题目所求,需要再判断一次。具体见代码。
//#include <bits/stdc++.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <queue>
#include <string>
#include <set>
#include <stack>
#include <map>
#include <cmath>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
//LOOP
#define FF(i, a, b) for(int i = (a); i < (b); ++i)
#define FE(i, a, b) for(int i = (a); i <= (b); ++i)
#define REP(i, N) for(int i = 0; i < (N); ++i)
#define CLR(A,value) memset(A,value,sizeof(A))
//OTHER
#define PB push_back
#define RI(n) scanf("%d", &n)
#define RII(n, m) scanf("%d%d", &n, &m)
#define RIII(n, m, k) scanf("%d%d%d", &n, &m, &k)
typedef long long LL;
typedef unsigned long long ULL;
typedef vector <int> VI;
const int INF = 0x3f3f3f3f;
const double EPS = 1e-9;
const int MOD = 1000000007;
const double PI = acos(-1.0);
const int maxn = 200010;
int n, M, rt1, rt2, mdrt;
VI G[maxn];
int num[maxn], fa[maxn], pre[maxn];
//指定深度任选的一个点,第一次bfs的父亲结点,第二次bfs父亲结点
bool vis[maxn];
void init()
{
REP(i, n + 1) G[i].clear();
}
struct Node{
int id, d;
Node() {}
Node(int a, int b) : id(a), d(b) {}
}t;
int bfs(int s, int ff[])
{
CLR(vis, 0);
queue<Node> Q;
Q.push(Node(s, 0));
vis[s] = 1, ff[s] = 0;
int md = 0;
while (!Q.empty())
{
t = Q.front(); Q.pop();
int u = t.id;
num[t.d] = u;
md = max(md, t.d);
REP(i, G[u].size())
{
int v = G[u][i];
if (!vis[v])
{
vis[v] = 1;
ff[v] = u;
Q.push(Node(v, t.d + 1));
}
}
}
return md;
}
int fun() //标记两个设备能到达的点
{
CLR(vis, 0);
int cnt = 0;
queue<Node> Q;
vis[rt1] = vis[rt2] = 1;
Q.push(Node(rt1, 0)), Q.push(Node(rt2, 0));
while (!Q.empty())
{
t = Q.front(); Q.pop();
if (t.d >= M) continue;
int u = t.id;
REP(i, G[u].size())
{
int v = G[u][i];
if (!vis[v])
{
vis[v] = 1;
Q.push(Node(v, t.d + 1));
}
}
}
FE(i, 1, n)
if (vis[i])
cnt++;
return cnt;
}
bool ok()
{
rt1 = mdrt;
REP(i, M) rt1 = pre[rt1];
int md = bfs(rt1, fa);
rt2 = num[md];
REP(i, M) rt2 = fa[rt2];
int cnt = fun();
if (cnt >= n)
{
if (rt1 == rt2) //特判两个点相等时
{
if (rt1 + 1 <= n) rt2 = rt1 + 1;
else rt2 = rt1 - 1;
}
return 1;
}
return 0;
}
int main()
{
int T, x, y;
RI(T);
while (T--)
{
RI(n);
init();
REP(i, n - 1)
{
RII(x, y);
G[x].PB(y), G[y].PB(x);
}
int md = bfs(1, pre);
mdrt = num[md];
int L = 0, R = md;
while (L <= R)
{
M = (L + R) >> 1;
if (ok()) R = M - 1;
else L = M + 1;
}
M = L; ok(); //求得最小距离后再判断一次得出放置设备点
printf("%d %d %d\n", L, rt1, rt2);
}
return 0;
}
/*
8
8
1 2
1 3
2 4
2 5
3 6
5 7
5 8
ans: 2 1 2
*/