题意:
给你一棵树求两个点使得树上所有点到这两个点路径长度的最小值最小 思路一:很容易想到从树的中心分开成两棵树,再找两棵树的中心。O(n)
要考虑树的拆分
注意到不一定是二叉
树的拆分:
首先树的中心一定在直径上
对于类似
1——2——3——4
中心为边,删除中心边即可
对于类似
1——2——3——4——5
中心为点,任意删除中心的一侧即可
在树的直径上二分距离并check。O(nlogn)
好懂好写
代码:
思路一:
#include <bits/stdc++.h>
using namespace std;
const int MAXN=2e5+10;
vector <int> edges[MAXN];
int fa[MAXN];
int dep[MAXN];
int get_far(int pos){
queue <int> que;
que.push(pos);
dep[pos]=1;
int last;
while(!que.empty()){
int now=que.front();que.pop();
last=now;
int len=edges[now].size();
for(int i=0;i<len;i++){
if(!dep[edges[now][i]]){
fa[edges[now][i]]=now;
que.push(edges[now][i]);
dep[edges[now][i]]=dep[now]+1;
}
}
}
return last;
}
int get_center(int pos){
memset(dep,0,sizeof(dep));
int far=get_far(pos);
memset(dep,0,sizeof(dep));
far=get_far(far);
int shift=dep[far]/2,mid=far;
if(dep[far]%2==0) shift--;
while(shift--){
mid=fa[mid];
}
return mid;
}
int main()
{
int T,n,x,y;
scanf("%d",&T);
for(int Case=1;Case<=T;Case++){
scanf("%d",&n);
for(int i=1;i<=n;i++) edges[i].clear();
for(int i=1;i<n;i++){
scanf("%d%d",&x,&y);
edges[x].push_back(y);
edges[y].push_back(x);
}
int center=get_center(1);
for(vector <int> ::iterator it=edges[center].begin();it!=edges[center].end();it++)
if(*it==fa[center]){edges[center].erase(it);break;}
for(vector <int> ::iterator it=edges[fa[center]].begin();it!=edges[fa[center]].end();it++)
if(*it==center){edges[fa[center]].erase(it);break;}
int ans1=get_center(fa[center]);
int dis=dep[ans1];
int ans2=get_center(center);
dis=max(dis,dep[ans2])-1;
printf("%d %d %d\n",dis,ans1,ans2);
}
}