• 题解 【核心城市】


    【题目描述】

    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 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;
    }
    
  • 相关阅读:
    rsync+inotify-tools实时备份脚本
    rsync+inotify实现实时同步
    linux 上安装部署python
    rsync全网备份low方法
    rsync 参数说明及使用参数笔记好文摘抄
    rsync 参数说明及使用参数笔记
    js DOM
    导出Excel
    Linux(CentOS 8)安装docker
    Win10安装虚拟机
  • 原文地址:https://www.cnblogs.com/LCGUO/p/12503639.html
Copyright © 2020-2023  润新知