核心城市

洛咕

题意:给定一棵n个节点的树,每条边的长度为1,钦定k个节点为核心节点,这k个节点要满足两个条件:

1.这k座城市可以通过边,在不经过其他节点的情况下两两相互到达.

2.定义某个非核心节点与这k个核心节点的距离为这个节点与k个核心节点的距离的最小值,那么所有非核心节点中,与核心节点的距离最大的节点,其与核心节点的距离最小.你需要求出这个最小值.

因为我是通过搜索标签搜到这道题的,所以一开始就往"树的直径"上面想了.这k个节点必定是树的直径上的一段,且k个节点的中点就是树的直径的中点.

所以我们先两次\(DFS\)找出树的直径,第二次\(DFS\)时通过记录路径\(pre\),找到树的直径的中点.然后以这个中点为根再一次\(DFS\),处理出\(dis[i]\)表示根节点到节点i的距离,\(dist[i]\)表示以节点i为根的子树中,从i出发能够到达的最远距离.

然后我们把所有节点按照\(dist[i]-dis[i]\)从大到小排序,前k个就是所谓核心节点了.

因为各种各样的原因,这道题我压行了,因为我发现不压行的话代码又长又丑,压行的话只是丑一点...

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define ll long long
using namespace std;
inline int read(){
    int x=0,o=1;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')o=-1,ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*o;
}
const int N=1e5+5;
int dis[N],pre[N],a[N],c[N],dist[N],bj[N];
int tot,head[N],nxt[N<<1],to[N<<1];
struct ppx{int val,id;}b[N];
inline bool cmp(ppx x,ppx y){return x.val>y.val;}
inline void add(int a,int b){nxt[++tot]=head[a];head[a]=tot;to[tot]=b;}
inline void dfs1(int u,int fa){
	for(int i=head[u];i;i=nxt[i]){
		int v=to[i];if(v==fa)continue;
		dis[v]=dis[u]+1;dfs1(v,u);
	}
}
inline void dfs2(int u,int fa){
	for(int i=head[u];i;i=nxt[i]){
		int v=to[i];if(v==fa)continue;
		dis[v]=dis[u]+1;pre[v]=u;dfs2(v,u);
	}
}
inline void dfs3(int u,int fa){
	dist[u]=dis[u];
	for(int i=head[u];i;i=nxt[i]){
		int v=to[i];if(v==fa)continue;
		dis[v]=dis[u]+1;dfs3(v,u);
		dist[u]=max(dist[u],dist[v]);
	}
}
int main(){
	int n=read(),k=read();
	for(int i=1;i<n;++i){int a=read(),b=read();add(a,b);add(b,a);}//存图
	dfs1(1,0);int maxn=0,pos1,pos2;//第一次以任意一个节点为根dfs
	for(int i=1;i<=n;++i)if(dis[i]>maxn){maxn=dis[i];pos1=i;}//找到距离最远的那个点pos1
	memset(dis,0,sizeof(dis));dfs2(pos1,0);maxn=0;//以pos1为根第二次dfs
	for(int i=1;i<=n;++i)if(dis[i]>maxn){maxn=dis[i];pos2=i;}//找到距离最远的那个节点pos2,此时pos1-pos2就是树的直径
	int tot=0;while(pos2!=pos1){a[++tot]=pos2;pos2=pre[pos2];}//递归存储直径上的所有节点
	a[++tot]=pos1;reverse(a+1,a+tot+1);
	int root=a[(tot+1)/2];memset(dis,0,sizeof(dis));dfs3(root,0);//找到中点,以其为根dfs
	for(int i=1;i<=n;++i){
		b[i].val=dist[i]-dis[i];
		b[i].id=i;c[i]=b[i].val;
	}
	sort(b+1,b+n+1,cmp);
	for(int i=1;i<=k;++i)bj[b[i].id]=1;//选出k个核心节点
	int ans=0;
	for(int i=1;i<=n;++i)
		if(!bj[i])ans=max(c[i]+1,ans);//不是核心节点,就计算贡献
	printf("%d\n",ans);
    return 0;
}

posted on 2019-09-21 16:23  PPXppx  阅读(225)  评论(0编辑  收藏  举报