由於沒有辦法複製過來,而我又很懶 所以只能大家自己去找了
題解
這道題目其實有點偏結論
首先 兩個人的話 他們最終集中的點肯定在這兩個人之間的路徑上
那麼這個時候再引入第三個人
將樣例畫出來 稍微變通一下就會發現
假如你多跑兩次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);
}
}