洛谷 【XR-3】核心城市(树的直径,树形dp)
传送门
解题思路
先考虑一个点。肯定是在树的直径的中间rt。
树的直径就是树上距离最远的两个点的路径。
对于树的直径求法,常用的有两种。
- 两遍bfs或dfs,从任意一个点开始,找到距离这个点最远的点i,再从i开始,找到距离i这个点距离最远的点j,则i、j就是树的直径的两个端点(证明略)。
- 树形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; }