【题目描述】
X 国有 (n) 座城市,(n − 1) 条长度为 (1) 的道路,每条道路连接两座城市,且任意两座城市都能通过若干条道路相互到达,显然,城市和道路形成了一棵树。
X 国国王决定将 (k) 座城市钦定为 X 国的核心城市,这 (k) 座城市需满足以下两个条件:
- 这 (k) 座城市可以通过道路,在不经过其他城市的情况下两两相互到达。
- 定义某个非核心城市与这 (k) 座核心城市的距离为,这座城市与 (k) 座核心城市的距离的最小值。那么所有非核心城市中,与核心城市的距离最大的城市,其与核心城市的距离最小。你需要求出这个最小值。
【输入格式】
第一行 (2) 个正整数 (n,k)。
接下来 (n - 1) 行,每行 (2) 个正整数 (u,v),表示第 (u) 座城市与第 (v) 座城市之间有一条长度为 (1) 的道路。
【输出格式】
一行一个整数,表示答案。
【输入样例】
1 2
2 3
2 4
1 5
5 6
【输出样例】
1
【样例解释】
钦定 (1,2,5) 这 (3) 座城市为核心城市,这样 (3,4,6) 另外 (3) 座非核心城市与核心城市的距离均为 (1),因此答案为 (1)。
【数据规模与约定】
- (1 le k < n le 10 ^ 5)。
- (1 le u,v le n, u e v),保证城市与道路形成一棵树。
这题的前置知识是会求直径。
我们可以显然证明,直径的中点肯定是 (k) 座核心城市之一。
伪证证明:
我们可以假设一个节点 (x) 是直径外,离直径最近的一个点。
显然,直径上的任意一个点到核心城市的距离,就是他到 (x) 的距离。
并且由于直径是树中最长的一条路径,所以没有其他点到 (x) 的距离大于直径端点到 (x) 的距离。
所以 (x) 在直径的时候,可以使与核心城市的距离最大的城市,其与核心城市的距离最小。
再证明 (x) 必须为直径的中点。
当 (x) 不为直径的中点时,必定有一个直径端点到 (x) 的距离稍稍的大那么一点点,只有当 (x) 为直径中点时,两个直径端点到 (x) 的最大值才会最小。
在找直径的时候,记录每个点的父亲,找到端点后,进行回溯,找到直径的中点。
然后贪心的思想,对于每个节点 (i),按照,以直径的中点为根,设 (deep_i) 为 (i) 节点的深度,(maxdeep_i) 表示 (i) 能到的最大深度。
按照能到达的最大深度减该节点的深度排序,取前 (k) 个数,就可以保证,与核心城市的距离最大的城市,其与核心城市的距离最小。
而且所选的点肯定是联通的。
代码如下:
#include<bits/stdc++.h>
#define rint register int
using namespace std;
int read(){
int s=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=0;c=getchar();}
while(c>='0'&&c<='9')s=(s<<1)+(s<<3)+(c^48),c=getchar();
return f?s:-s;
}
int n,k,dis[100010],vis[100010],point,faq;
int tot,head[200010],ver[200010],nxt[200010];
int pl[100010],maxdeep[100010],deep[100010];
int ans[100010],minn=-1;
bool cmp(int x,int y){
return x>y;
}
void add(int x,int y){
nxt[++tot]=head[x]; ver[tot]=y;
head[x]=tot;
}
void bfs(int s){ //找直径
memset(dis,0,sizeof dis);
memset(vis,0,sizeof vis);
queue<int>q; q.push(s); vis[s]=1;
while(q.size()) {
int x=q.front(); q.pop();
for(rint i=head[x];i;i=nxt[i]){
int y=ver[i];
if(!vis[y]){
pl[y]=x;
dis[y]=dis[x]+1;
q.push(y); vis[y]=1;
}
}
}
faq=0;
for(rint i=1;i<=n;++i)
if(dis[i]>faq) faq=dis[i],point=i;
}
void dfs(int x,int fa){ //处理深度
maxdeep[x]=deep[x];
for(rint i=head[x];i;i=nxt[i]){
int y=ver[i];
if(y==fa) continue;
deep[y]=deep[x]+1; dfs(y,x);
maxdeep[x]=max(maxdeep[x],maxdeep[y]);
}
}
int main(){
n=read(); k=read();
for(rint i=1,x,y;i<n;++i){
x=read(); y=read();
add(x,y); add(y,x);
}
bfs(1); bfs(point);
int mid_point=point;
for(rint i=1;i<=faq+1>>1;++i) mid_point=pl[mid_point];
dfs(mid_point,0);
for(rint i=1;i<=n;++i) ans[i]=maxdeep[i]-deep[i];
sort(ans+1,ans+1+n,cmp);
for(rint i=k+1;i<=n;++i) minn=max(ans[i]+1,minn);
printf("%d",minn);
return 0;
}