洛谷 【XR-3】核心城市(树的直径,树形dp)

传送门


解题思路

先考虑一个点。肯定是在树的直径的中间rt。

树的直径就是树上距离最远的两个点的路径。

对于树的直径求法,常用的有两种。

  1. 两遍bfs或dfs,从任意一个点开始,找到距离这个点最远的点i,再从i开始,找到距离i这个点距离最远的点j,则i、j就是树的直径的两个端点(证明略)。
  2. 树形dp也可以求。

再推广到n个点。以rt为根,树形dp,dis[i]表示以i为根的子树的深度,即把城市i作为核心城市后,子树i中距离i最远的点到核心城市群的距离。

然后把dis排序,删去最大的前k个,这k个就是核心城市群。然后第k+1大的dis再+1就是最终的答案。

为什么要+1呢?因为第k+1大的dis对应的城市到核心城市群还有1的距离,所以要加1.

AC代码

#include<iostream> 
#include<cstdio>
#include<cstring>
#include<queue>
#include<cmath> 
#include<algorithm>
using namespace std;
const int maxn=100005;
int n,k,dis[maxn][3],diss[maxn],cnt,p[maxn],d1,d2,rt;
struct node{
    int v,next;
}e[maxn*2];
void insert(int u,int v){
    cnt++;
    e[cnt].next=p[u];
    e[cnt].v=v;
    p[u]=cnt;
}
int dfs(int u,int id){
    int ans;
    queue<int> q;
    dis[u][id]=0;
    q.push(u);
    while(!q.empty()){
        u=q.front();
        q.pop();
        if(q.empty()){
            ans=u;
        }
        for(int i=p[u];i!=-1;i=e[i].next){
            int v=e[i].v;
            if(!dis[v][id]){
                dis[v][id]=dis[u][id]+1;
                q.push(v);
            }
        }
    }
    return ans;
}
void findd(){
    d1=dfs(1,0);
    d2=dfs(d1,1);
    for(int i=1;i<=n;i++){
        if(abs(dis[i][0]-dis[i][1])<=1){
            rt=i;
            break;
        }
    }
}
void dfs2(int u,int fa){
    for(int i=p[u];i!=-1;i=e[i].next){
        int v=e[i].v;
        if(v==fa) continue;
        dfs2(v,u);
        diss[u]=max(diss[u],diss[v]+1);
    }
}
int main(){
    memset(p,-1,sizeof(p));
    cin>>n>>k;
    for(int i=1;i<n;i++){
        int u,v;
        cin>>u>>v;
        insert(u,v);
        insert(v,u);
    }
    findd();
    dfs2(rt,-1);
    sort(diss+1,diss+n+1);
    cout<<diss[n-k]+1<<endl;
    return 0;
}

 

posted @ 2020-09-13 21:46  尹昱钦  阅读(160)  评论(0编辑  收藏  举报