题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=5374
大致题意:给你一棵树,让你求出树上的两点,使得树上的其他点到这个点的最大距离最小。
思路:同步赛的时候想到了,一个点的情况应该是树上的最长链(直径)的中点(这个仔细想一下就能证明出来),但是两个点的情况就gg了,当时也想到了将最长链的中点分成两份,然后再求中点,但是感觉不太对就没有去敲,转而去想了其他的思路。
证明:
首先能证明两个点一定分布在直径中点两侧 因为如果是同侧的话,答案就会大于直径的二分一 然后从中点将树分成两棵,基于贪心的方法,如果我们不选取两棵树直径的中点的话 那么原树上的最大值一定大于两棵子树直径的一半的最大值,所以应该选两个中点 (非严格证明了~)
写得时候感觉姿势特别挫,感觉又事要K好久,谁知道wa了一炮就过了,开心呀~
code:
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <cstring>
#define INF 1000000000
using namespace std;
const int maxn=200500;
const int maxe=500500;
struct edge
{
int to,next;
} P[maxe];
int head[maxn],si;
int dis[maxn],que[maxe],pre[maxn],nn;
void add_edge(int s,int t)
{
P[si].to=t;
P[si].next=head[s];
head[s]=si++;
}
void bfs(int u)
{
for(int i=0;i<=nn;i++) dis[i]=INF;
int hh,tt;
hh=tt=0;
que[tt++]=u;
dis[u]=0;
while(hh!=tt){
int v=que[hh++];
for(int i=head[v];i!=-1;i=P[i].next){
int mid=P[i].to;
if(mid==-1) continue;
if(dis[v]+1<dis[mid]){
dis[mid]=dis[v]+1;
pre[mid]=v;
que[tt++]=mid;
}
}
}
}
int mid_of_tree(int u,int &mark)
{
int he,ta,mid,res;
bfs(u);
he=u;
for(int i=1;i<=nn;i++) if(dis[i]!=INF&&dis[i]>dis[he]) he=i;
memset(pre,-1,sizeof(pre));
bfs(he);
ta=he;
for(int i=1;i<=nn;i++) if(dis[i]!=INF&&dis[i]>dis[ta]) ta=i;
res=ta;
for(int kk=0;kk<dis[ta]/2;kk++) res=pre[res];
mark=dis[ta];
return res;
}
void solve()
{
int pp,pp1,pp2,dis1,dis2,mid;
pp=mid_of_tree(1,dis1);
for(int i=head[pp];i!=-1;i=P[i].next){
if(P[i].to==pre[pp]){
P[i].to=-1;
P[i^1].to=-1;
}
}
mid=pre[pp];
pp1=mid_of_tree(pp,dis1);
pp2=mid_of_tree(mid,dis2);
printf("%d %d %d\n",max((dis1+1)/2,(dis2+1)/2),pp1,pp2);
}
int main()
{
int T,s,t;
scanf("%d",&T);
while(T--){
scanf("%d",&nn);
memset(head,-1,sizeof(head));
si=0;
for(int kk=0;kk<nn-1;kk++){
scanf("%d%d",&s,&t);
add_edge(s,t);
add_edge(t,s);
}
solve();
}
return 0;
}