由于没有办法复制过来,而我又很懒 所以只能大家自己去找了
题解
这道题目其实有点偏结论
首先 两个人的话 他们最终集中的点肯定在这两个人之间的路径上
那么这个时候再引入第三个人
将样例画出来 稍微变通一下就会发现
假如你多跑两次LCA的话
最终的结果的两倍就是三条路径长度之和
另外还有几个结论
最终集合的点一定是三个人两两公共祖先中的一个(这个应该不需要证明)
且这个点就是三个公共祖先中不同的那个点
小小地证明一下
每个点在集合时 肯定是要经过和其它的点的公共祖先的
当我们选择相同的那个点作为时
就会有两个人经过不同的那个点最后到达相同的那个点
(因为有两条路径是经过了那个点的,不同的点走过的路径要单独看)
反之 就只有一个点经过相同的点病到达不同的点
不同的点和相同的点之间的路径是相同的
所以 相比之下 后者更优
附上对拍代码
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
int n,m,resa,resb,x,y,z,maxf,fl;
int all=0,star[500123],nxt[1000123],ent[1000123];
int dep[500123],up[500123][20];
inline int input()
{
char c=getchar();int o;
while(c>57||c<48)c=getchar();
for(o=0;c>47&&c<58;c=getchar())o=(o<<1)+(o<<3)+c-48;
return o;
}
void add(int s,int e)
{
nxt[++all]=star[s];
star[s]=all;
ent[all]=e;
}
void search(int p,int f,int fa)
{
dep[p]=f;
up[p][0]=fa;
for(int i=1;i<=18;i++)up[p][i]=up[up[p][i-1]][i-1];
for(int bian=star[p],e=ent[bian];bian;bian=nxt[bian],e=ent[bian])
if(e!=fa)search(e,f+1,p);
}
int LCA(int a,int b)
{
if(dep[a]<dep[b])swap(a,b);
fl=dep[a]-dep[b];
for(int i=0;i<=18;i++)
if(fl&(1<<i))a=up[a][i];
if(a==b)return a;
for(int c=18;c>=0;c--)
if(up[a][c]!=up[b][c])a=up[a][c],b=up[b][c];
return up[a][0];
}
int getd(int p)
{
return dep[p]+dep[resa]-dep[LCA(p,resa)]*2;
}
void mini(int fa,int fb,int fc)
{
if(fa==fb)resa=fc;
if(fb==fc)resa=fa;
if(fa==fc)resa=fb;
resb=getd(x)+getd(y)+getd(z);
}
int main()
{
// freopen("In.txt","r",stdin);
int ca,cb,cc,s,e;
n=input();m=input();
for(int i=1;i<n;i++)
{
s=input();e=input();
add(s,e);add(e,s);
}
search(1,1,0);
for(int i=1;i<=m;i++)
{
x=input();y=input();z=input();
ca=LCA(x,y);cb=LCA(x,z);cc=LCA(y,z);
mini(ca,cb,cc);
printf("%d %d\n",resa,resb);
}
}