【XR-3】核心城市
题目描述
X 国有 n n n 座城市, n − 1 n - 1 n−1 条长度为 1 1 1 的道路,每条道路连接两座城市,且任意两座城市都能通过若干条道路相互到达,显然,城市和道路形成了一棵树。
X 国国王决定将 k k k 座城市钦定为 X 国的核心城市,这 k k k 座城市需满足以下两个条件:
- 这 k k k 座城市可以通过道路,在不经过其他城市的情况下两两相互到达。
- 定义某个非核心城市与这 k k k 座核心城市的距离为,这座城市与 k k k 座核心城市的距离的最小值。那么所有非核心城市中,与核心城市的距离最大的城市,其与核心城市的距离最小。你需要求出这个最小值。
输入格式
第一行 2 2 2 个正整数 n , k n,k n,k。
接下来 n − 1 n - 1 n−1 行,每行 2 2 2 个正整数 u , v u,v u,v,表示第 u u u 座城市与第 v v v 座城市之间有一条长度为 1 1 1 的道路。
数据范围:
- 1 ≤ k < n ≤ 1 0 5 1 \le k < n \le 10 ^ 5 1≤k<n≤105。
- 1 ≤ u , v ≤ n , u ≠ v 1 \le u,v \le n, u \ne v 1≤u,v≤n,u=v,保证城市与道路形成一棵树。
输出格式
一行一个整数,表示答案。
样例 #1
样例输入 #1
6 3
1 2
2 3
2 4
1 5
5 6
样例输出 #1
1
提示
【样例说明】
钦定 1 , 2 , 5 1,2,5 1,2,5 这 3 3 3 座城市为核心城市,这样 3 , 4 , 6 3,4,6 3,4,6 另外 3 3 3 座非核心城市与核心城市的距离均为 1 1 1,因此答案为 1 1 1。
思路
- 首先,题目求的是:与核心城市的距离最大的城市,其与核心城市的距离最小,我们直接的思路就是找到直径,然后找到中点,然后再从中点向下枚举,这样就能使它的值最小了。
- 这种只要找深度即可。(因为每个城市间的距离跟深度有关系(前提是找到中点))
- 拓展:求树的直径有两种方法:2遍dfs、树形dp。
代码
//首先先求出树的直径,然后找到中点,然后向下搜索即可
//一般用两边dfs,第一遍第一次DFS我们从任意一个节点开始走记录下离它最远的节点,第二遍是记录路径
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 1e5+10,M = 2*N;
int e[M],ne[M],w[M],h[N],idx;
bool st[N];
int pre[N];//记录路径
int dist[N],depth[N];
int maxdepth[N];
int n,m,maxv;//深度最大值
int ans[N];
int num;//记录最远的编号
void add(int a,int b){
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void dfs1(int u,int fa){
if(depth[u]>maxv){
maxv=depth[u];
num=u;
}
for(int i=h[u];~i;i=ne[i]){
int j=e[i];
//求树的直径都是这样写的,不能回头
if(j==fa)continue;
depth[j]=depth[u]+1;
dfs1(j,u);
}
}
void dfs2(int u,int fa){
if(depth[u]>maxv){
maxv=depth[u];
num=u;
}
for(int i=h[u];~i;i=ne[i]){
int j=e[i];
//求树的直径都是这样写的,不能回头
if(j==fa)continue;
depth[j]=depth[u]+1;
pre[j]=u;//记录路径
dfs2(j,u);
}
}
void dfs3(int u,int fa){
maxdepth[u]=depth[u];
for(int i=h[u];~i;i=ne[i]){
int j=e[i];
if(j==fa)continue;
depth[j]=depth[u]+1;
dfs3(j,u);
maxdepth[u]=max(maxdepth[u],maxdepth[j]);
}
}
int main(){
cin>>n>>m;
memset(h,-1,sizeof h);
for(int i=1;i<n;i++){
int a,b;
cin>>a>>b;
add(a,b);
add(b,a);
}
dfs1(1,-1);
maxv=0;
memset(depth,0,sizeof depth);
dfs2(num,-1);
int k=num;
//找中点
//路径往回跳
for(int i=1;i<=(depth[num]+1)/2;i++)k=pre[k];
memset(depth,0,sizeof depth);
//找最大深度,同时以当前点为根节点进行depth
dfs3(k,-1);
for(int i=1;i<=n;i++)ans[i]=maxdepth[i]-depth[i];
sort(ans+1,ans+1+n,greater<int>());
int res=0;
for(int i=m+1;i<=n;i++){
res=max(res,ans[i]+1);
}
cout<<res;
return 0;
}