【XR-3】核心城市
题目描述
X 国有 n 座城市,n−1 条长度为 1 的道路,每条道路连接两座城市,且任意两座城市都能通过若干条道路相互到达,显然,城市和道路形成了一棵树。
X 国国王决定将 k 座城市钦定为 X 国的核心城市,这 k 座城市需满足以下两个条件:
- 这 k 座城市可以通过道路,在不经过其他城市的情况下两两相互到达。
- 定义某个非核心城市与这 k 座核心城市的距离为,这座城市与 k 座核心城市的距离的最小值。那么所有非核心城市中,与核心城市的距离最大的城市,其与核心城市的距离最小。你需要求出这个最小值。
输入格式
第一行 2 个正整数 n,k。
接下来 n−1 行,每行 2 个正整数 u,v,表示第 u 座城市与第 v 座城市之间有一条长度为 1 的道路。
题目比较绕,其实就是选k个点(k个点相邻),让其他没被选的点到这k个点的距离最大值最小(距离为到最近的核心城市距离);
这题没想到是要用到树的直径;
可以推出必有一个点在树的直径的中点上,然后以这个中点为根再dfs一遍,目的是找出所有子节点的子树的最大深度,然后取第k+1个就行(前k个做为核心城市);
代码:
#include<bits/stdc++.h>
#define LL long long
#define pa pair<int,int>
#define ls k<<1
#define rs k<<1|1
#define inf 0x3f3f3f3f
using namespace std;
const int N=100010;
const int M=2000100;
const LL mod=1e9+7;
int n,k,cnt,head[N],ans=0;
int st1,st2,max1,max2,len1[N],len2[N],len3[N],pre[N];
vector<int>ve;
bool cmp(int x,int y){return x>y;}
struct Node{
int to,nex;
}edge[M];
void add(int p,int q){
edge[cnt].to=q;
edge[cnt].nex=head[p];
head[p]=cnt++;
}
void dfs1(int sn,int fa){
if(len1[sn]>=max1){
max1=len1[sn];
st1=sn;
}
for(int i=head[sn];~i;i=edge[i].nex){
int v=edge[i].to;
if(v!=fa) len1[v]=len1[sn]+1,dfs1(v,sn);
}
}
void dfs2(int sn,int fa){
if(len2[sn]>=max2){
max2=len2[sn];
st2=sn;
}
for(int i=head[sn];~i;i=edge[i].nex){
int v=edge[i].to;
if(v!=fa){
pre[v]=sn;
len2[v]=len2[sn]+1;
dfs2(v,sn);
}
}
}
void dfs3(int sn,int fa){
len3[sn]=0;
for(int i=head[sn];~i;i=edge[i].nex){
int v=edge[i].to;
if(v!=fa){
dfs3(v,sn);
len3[sn]=max(len3[sn],len3[v]+1);
}
}
}
int main(){
memset(head,-1,sizeof(head));
scanf("%d%d",&n,&k);
for(int i=1;i<n;i++){
int u,v;
scanf("%d%d",&u,&v);
add(u,v),add(v,u);
}
dfs1(1,-1);
dfs2(st1,-1);
for(int i=1;i<=(max2+1)/2;i++) st2=pre[st2];//找到直径中点
dfs3(st2,-1);
for(int i=1;i<=n;i++) ve.push_back(len3[i]);
sort(ve.begin(),ve.end(),cmp);
cout<<ve[k]+1<<endl;//第k+1个点明显最大
return 0;
}