描述
H 国有 n 座城市和 n-1 条无向道路,保证每两座城市都可以通过道路互相到达。现在 H 国要开始施工,施工分若干个阶段,第 i 个阶段会建设无向道路 (x,y) ,当且仅当存在一个数 z,满足 x ≠ z, x ≠ y, z ≠ y,且在第 i-1 个阶段后,存在无向道路 (x,z), (z,y).
现在 H 国的国王想知道,在几个阶段后,每两个不同的城市之间都有一条无向道路.
输入
第一行一个正整数 n
接下来 n-1 行,每行两个正整数 (x,y),描述一开始的一条无向道路 (x,y)
1 ≤ n ≤ 105
输出
输出最少几个阶段后,每两个不同的城市之间都有一条无向道路.
样例输入
3
1 2
2 3
样例输出
1
思路:
可以发现:一开始只有树上距离小于等于 1 的点对 (x,y) 之间有道路
然后第 1 个阶段后,所有树上距离小于等于 2 的点对 (x,y) 之间也有道路了
同理,第 i 个阶段后,所有树上距离小于等于 2i 2 i 的点对 (x,y) 之间都有道路了
于是我们只需要求出树的直径的长度 d d ,暴力求出几个阶段后有, 主要是怎么求树的直径。
假设以u为起点,用dfs或者bfs遍历这棵树,找到距离u最远的点s,然后再以s为起点遍历这棵树,找到最远的点t,点 s,t的距离就是树的直径
证明:
假设 s-t这条路径为树的直径,或者称为树上的最长路
现有结论,从任意一点u出发搜到的最远的点一定是s、t中的一点,然后在从这个最远点开始搜,就可以搜到另一个最长路的端点,即用两遍广搜就可以找出树的最长路
证明:
1 设u为s-t路径上的一点,结论显然成立,否则设搜到的最远点为T则
dis(u,T) >dis(u,s) 且 dis(u,T)>dis(u,t) 则最长路不是s-t了,与假设矛盾
2 设u不为s-t路径上的点
首先明确,假如u走到了s-t路径上的一点,那么接下来的路径肯定都在s-t上了,而且终点为s或t,在1中已经证明过了
所以现在又有两种情况了:
1:u走到了s-t路径上的某点,假设为X,最后肯定走到某个端点,假设是t ,则路径总长度为dis(u,X)+dis(X,t)
2:u走到最远点的路径u-T与s-t无交点,则dis(u-T) >dis(u,X)+dis(X,t);显然,如果这个式子成立,
则dis(u,T)+dis(s,X)+dis(u,X)>dis(s,X)+dis(X,t)=dis(s,t)最长路不是s-t矛盾
#include <cstdio>
#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
const int maxn = 1e5 + 5;
vector<int>g[maxn];
int d[maxn];
bool vis[maxn];
void dfs(int u)
{
vis[u] = 1;
for (auto &v: g[u]) {
if (vis[v]) continue;
d[v] = d[u]+1;
dfs(v);
}
}
int main()
{
memset(d,0,sizeof(d));
memset(vis,0,sizeof(vis));
int n,u,v;
cin >> n;
for (int i = 1; i < n; ++i) {
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
dfs(1);
int len = 0,st;
for (int i = 1; i <= n; ++i) {
if (d[i] > len) {
st = i;
len = d[i];
}
}
memset(d,0,sizeof(d));
memset(vis,0,sizeof(vis));
len = 0;
dfs(st);
for (int i = 1; i <= n; ++i)
len = max(len,d[i]);
int k = 1;
int res = 0;
while(k < len) {
k *= 2;
++res;
}
cout << res << endl;
return 0;
}