这道题莫名眼熟,于是马上猜到了结论:
对于一棵树中,要求到三个点x,y,z总距离最小的点,那么,这个点一定是
lca(x,y),lca(x,z),lca(y,z)这三个点中的一个(一开始我还算了lca(x,y,z),后来发现好像不用。。。)
所以,我们可以打个lca,每次再比较下到哪个点距离最小即可~
update:补一下结论的证明
我们将情况分为以下三种:
1.
画个图不难发现,若我们将A上移一下,那么,距离和会+3。
若我们下移,分两种情况:
①向有x/y/z的儿子下移,距离和+2
②向没有x/y/z的儿子下移,距离和+3
所以距离和最小的点就是A
2.
不难证明,若要满足此情况,则A一定为B的真祖先
同样,我们分三种情况:
①B向上移动则,距离和+2-1(y,z距离+1,x距离-1)
②B向有y/z的儿子下移,距离和+2-1(y/z距离-1,z/y,x距离+1)
③B向没有y/z的儿子下移,距离和+3
所以距离和最小的就是B
由于x,y,z具有对称性,所以其他同理
3.
不难证明,不存在这种情况,证明如下:
设
若z不在A的子树里面,则
若z在A的子树里面,则:
①x和z在A的同一个儿子子树里面,可以转化为:,y不在B的子树,所以有
y和z在A的同一个儿子子树同理
②z和x和y都不在不在A的同一个儿子子树里面,则一定有:
命题得证。
所以综上所述,答案一定是三个点两两lca的点中的一个
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+1;
struct node{
int v,nex;
}t[N<<1];
int las[N],len;
inline void add(int u,int v){
t[++len]=(node){v,las[u]},las[u]=len;
}
int fa[N][19],dep[N];
inline void dfs(int now,int fu){
fa[now][0]=fu;
for(int i=1;i<=18;++i){
fa[now][i]=fa[fa[now][i-1]][i-1];
if(!fa[now][i]){
break;
}
}
for(int i=las[now];i;i=t[i].nex){
int v=t[i].v;
if(v!=fu){
dep[v]=dep[now]+1;
dfs(v,now);
}
}
}
inline int lca(int x,int y){
if(dep[x]<dep[y]){
x^=y^=x^=y;
}
int cha=dep[x]-dep[y];
for(int i=18;~i;--i){
if(cha>=(1<<i)){
cha-=(1<<i);
x=fa[x][i];
}
}
if(x==y){
return x;
}
for(int i=18;~i;--i){
if(fa[x][i]!=fa[y][i]){
x=fa[x][i],y=fa[y][i];
}
}
return fa[x][0];
}
inline int dis(int x,int y){
int z=lca(x,y);
return dep[x]+dep[y]-2*dep[z];
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<n;++i){
int u,v;
scanf("%d%d",&u,&v);
add(u,v),add(v,u);
}
dep[1]=1;
dfs(1,0);
while(m--){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
int res=2e9,gol=0,now=0,fow=0;
now=lca(x,y);fow=dis(x,now)+dis(y,now)+dis(z,now);
if(res>fow){
res=fow;gol=now;
}
now=lca(x,z);fow=dis(x,now)+dis(y,now)+dis(z,now);
if(res>fow){
res=fow;gol=now;
}
now=lca(y,z);fow=dis(x,now)+dis(y,now)+dis(z,now);
if(res>fow){
res=fow;gol=now;
}
now=lca(x,lca(y,z));fow=dis(x,now)+dis(y,now)+dis(z,now);
if(res>fow){
res=fow;gol=now;
}
printf("%d %d\n",gol,res);
}
return 0;
} 
京公网安备 11010502036488号