AHOI2008 紧急集合
sol:
如果只有两个点,那么显然目的地就是在他们二者路径上的任意一点。
现在有三个点,考虑两两的路径和lca,发现肯定有两对求得的lca相同,另外一对的lca深度比那两对的lca深度大。
这个深度大一些的那个lca就是目的地(最近点),最小距离就是三者两两距离的二分之一。
所以直接树上倍增即可。
#include<bits/stdc++.h>
#define IL inline
#define RG register
#define DB double
#define LL long long
using namespace std;
IL int gi() {
RG int x=0,p=1; RG char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') p=-1;ch=getchar();}
while(ch>='0'&&ch<='9') x=x*10+(ch^48),ch=getchar();
return x*p;
}
const int N=5e5+1;
int n,m,tot,len,npos,dep[N],head[N],f[N][20];
struct LCA{int pos,dis;}s[5];
struct EDGE{int next,to;}e[N<<1];
IL void make(int a,int b) {
e[++tot]=(EDGE){head[a],b},head[a]=tot;
e[++tot]=(EDGE){head[b],a},head[b]=tot;
}
void dfs(int x,int fx) {
RG int i,y;
for(i=head[x];i;i=e[i].next)
if((y=e[i].to)!=fx)
dep[y]=dep[x]+1,f[y][0]=x,dfs(y,x);
}
IL void multiply() {
RG int i,j;
for(i=1;i<20;++i)
for(j=1;j<=n;++j)
f[j][i]=f[f[j][i-1]][i-1];
}
IL LCA lca(int x,int y) {
RG int i,dis=0;
if(dep[x]<dep[y]) swap(x,y);
for(i=19;i>=0;--i)
if(dep[f[x][i]]>=dep[y]) dis+=1<<i,x=f[x][i];
if(x==y) return (LCA){x,dis};
for(i=19;i>=0;--i)
if(f[x][i]!=f[y][i])
dis+=1<<i+1,x=f[x][i],y=f[y][i];
return (LCA){f[x][0],dis+2};
}
int main()
{
RG int i,x,y,z;
n=gi(),m=gi();
for(i=1;i<n;++i) x=gi(),y=gi(),make(x,y);
dep[1]=1,dfs(1,1),multiply();
while(m--) {
x=gi(),y=gi(),z=gi();
s[1]=lca(x,y),s[2]=lca(y,z),s[3]=lca(x,z);
len=(s[1].dis+s[2].dis+s[3].dis)/2;
for(i=1,npos=0;i<=3;++i)
if(dep[s[i].pos]>dep[npos]) npos=s[i].pos;
printf("%d %d\n",npos,len);
}
return 0;
}