[AHOI2008]紧急集合 / 聚会
题目大意:
给你一颗n个结点的树,然后给你m个3元组(x,y,z);知道到一个点,这个点到x,y,z的距离和最小;
结论:x,y,z两两求lca,会发现有两个lca相等,不相等的那个lca就是最小距离点,根据这个点求最小距离和即可;
代码:
#include<bits/stdc++.h>
#define LL long long
#define pa pair<int,int>
#define ls k<<1
#define rs k<<1|1
#define inf 0x3f3f3f3f
using namespace std;
const int N=500100;
const int M=50100;
const LL mod=10007;
int n,m,head[N],cnt,lg[N],dep[N],fa[N][40];
struct Node{
int to,nex;
}edge[N*2];
void add(int p,int q){edge[cnt].to=q,edge[cnt].nex=head[p],head[p]=cnt++;}
void dfs(int sn,int ft){
dep[sn]=dep[ft]+1,fa[sn][0]=ft;
for(int i=1;i<=lg[dep[sn]];i++) fa[sn][i]=fa[fa[sn][i-1]][i-1];
for(int i=head[sn];~i;i=edge[i].nex){
if(edge[i].to!=ft) dfs(edge[i].to,sn);
}
}
int lca(int x,int y){
if(dep[x]<dep[y]) swap(x,y);
while(dep[x]>dep[y]) x=fa[x][lg[dep[x]-dep[y]]-1];
if(x==y) return x;
for(int k=lg[dep[x]]-1;k>=0;k--){
if(fa[x][k]!=fa[y][k]){
x=fa[x][k],y=fa[y][k];
}
}
return fa[x][0];
}
int main(){
memset(head,-1,sizeof(head));
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++){
int a,b;scanf("%d%d",&a,&b);
add(a,b),add(b,a);
}
for(int i=1;i<=n;i++) lg[i]=lg[i-1]+(1<<lg[i-1]==i);
dfs(1,0);
for(int i=1;i<=m;i++){
int x,y,z;scanf("%d%d%d",&x,&y,&z);
int lcaxy=lca(x,y),lcaxz=lca(x,z),lcayz=lca(y,z);
if(lcaxy==lcaxz){//lcayz
int lenyz=dep[y]+dep[z]-2*dep[lcayz];
int lcayz_x=lca(lcayz,x);
int lenyz_x=dep[x]+dep[lcayz]-2*dep[lcayz_x];
printf("%d %d\n",lcayz,lenyz+lenyz_x);
}
else if(lcaxy==lcayz){//lcaxz
int lenxz=dep[x]+dep[z]-2*dep[lcaxz];
int lcaxz_y=lca(lcaxz,y);
int lenxz_y=dep[y]+dep[lcaxz]-2*dep[lcaxz_y];
printf("%d %d\n",lcaxz,lenxz+lenxz_y);
}
else{//lcaxy
int lenxy=dep[x]+dep[y]-2*dep[lcaxy];
int lcaxy_z=lca(lcaxy,z);
int lenxy_z=dep[z]+dep[lcaxy]-2*dep[lcaxy_z];
printf("%d %d\n",lcaxy,lenxy+lenxy_z);
}
}
return 0;
}