题解 【核心城市】

【题目描述】

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\) 的道路。

【输出格式】

一行一个整数,表示答案。

【输入样例】

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 \ne 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;
}
posted @ 2020-03-16 14:20  LCGUO  阅读(236)  评论(0编辑  收藏  举报