树的直径+思维【XR-3】核心城市(洛谷 P5536)

【XR-3】核心城市

题目描述

X 国有 n 座城市,n−1 条长度为 1 的道路,每条道路连接两座城市,且任意两座城市都能通过若干条道路相互到达,显然,城市和道路形成了一棵树。

X 国国王决定将 k 座城市钦定为 X 国的核心城市,这 k 座城市需满足以下两个条件:

  1. 这 k 座城市可以通过道路,在不经过其他城市的情况下两两相互到达。
  2. 定义某个非核心城市与这 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;
}

发布了264 篇原创文章 · 获赞 46 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_44291254/article/details/105187999