牛客多校第四场 A meeting 树的半径
题意:
有一棵树,树上有许多人,他们要聚会,找一个点使得所有人到这个点的距离的最大值最小。
题解:
首先,以一个有人的点为根,求一个生成树,删掉所有没有人的子树,保证所有的悬挂点(只连接一条边的点)都是有人的节点,以保证后面求出的直径的两端是两个有人节点。为什么非得以有人的节点为根呢?因为如果找了一个没人的节点当根,而这个根又恰好是一个悬挂点,那么可以想象,这个悬挂点可能成为直径的端点。
其次,求树的直径,就是树上两点的最长距离,这个直径的两个端点必然都是悬挂点。
直径/2向上取整就是半径,也就是题目所求距离。
求直径可跑两遍dfs,第一遍从任意点开始,找出与此点距离最大点,第二遍从这个点开始,找出与这个点距离最大点。
赛时想的是树的重心,wa了,想像一棵树上半部分是一条长链,下半部分是密密麻麻的儿子接在长链一端。然后又想了拓扑求直径,T了。
但是发现拓扑排序的轮数其实就是树的半径,不知道这个结论有什么用。
#include<iostream> #include<vector> #define INF 0x3f3f3f3f #include<queue> #include<cstring> #include<set> using namespace std; vector<int> lnk[100005]; vector<int> sclnk[100005];//生成树 int n; int scn;//生成树的节点数 bool ren[100005]; bool dfs(int fa,int u){ bool ok=ren[u]; for(int i=0;i<lnk[u].size();i++){ int v=lnk[u][i]; if(v==fa)continue; bool tr=dfs(u,v); if(tr){ sclnk[u].push_back(v); sclnk[v].push_back(u); scn++; } ok=ok||tr; } return ok; } int kg; void prt(int fa,int u){ //把生成树打印出来 for(int i=1;i<=kg;i++)printf(" "); printf("%d\n",u); kg++; for(int i=0;i<sclnk[u].size();i++){ int v=sclnk[u][i]; if(v==fa)continue; prt(u,v); } kg--; } bool vis[100005]; int size[100005]; int dis[100005]; struct P{ int n,dis; P(){} P(int n1,int dis1){ n=n1;dis=dis1; } friend bool operator >(const P &a,const P &b){ return a.dis>b.dis; } friend bool operator <(const P &a,const P &b){ return a.dis<b.dis; } }; void ddfs(int st) { vis[st] = 1; for(int i = 0;i<sclnk[st].size();i++) { int to =sclnk[st][i]; if(!vis[to]) { dis[to] = dis[st] +1; ddfs(to); } } } int main(){ //把一个有人的点揪出来当树根 //删掉无人的子树 //找出树的直径 int k; scanf("%d %d",&n,&k); for(int i=1;i<n;i++){ int a,b; scanf("%d %d",&a,&b); lnk[a].push_back(b); lnk[b].push_back(a); } int r1; for(int i=1;i<=k;i++){ int q; scanf("%d",&q); r1=q; ren[q]=1; } dfs(-1,r1); // prt(r1); //求生成树 memset(dis,INF,sizeof dis); dis[r1] = 0; ddfs(r1); //求树的直径 int maxi=r1; for(int i=1;i<=n;i++){ if(dis[i]!=INF && dis[i]>dis[maxi])maxi=i; } memset(dis,INF,sizeof dis); memset(vis,0,sizeof vis); dis[maxi] = 0; ddfs(maxi); int maxx=0; for(int i=1;i<=n;i++){ // printf("%d ",dis[i]); if(dis[i]!=INF)maxx=max(maxx,dis[i]); } printf("%d\n",(maxx+1)/2); return 0; }