这道题莫名眼熟,于是马上猜到了结论:
对于一棵树中,要求到三个点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; }