题目描述
欢乐岛上有个非常好玩的游戏,叫做“紧急集合”。在岛上分散有 n 个等待点,有 n−1 条道路连接着它们,每一条道路都连接某两个等待点,且通过这些道路可以走遍所有的等待点,通过道路从一个点到另一个点要花费一个游戏币。
参加游戏的人三人一组,开始的时候,所有人员均任意分散在各个等待点上(每个点同时允许多个人等待),每个人均带有足够多的游戏币(用于支付使用道路的花费)、地图(标明等待点之间道路连接的情况)以及对话机(用于和同组的成员联系)。当集合号吹响后,每组成员之间迅速联系,了解到自己组所有成员所在的等待点后,迅速在 n 个等待点中确定一个集结点,组内所有成员将在该集合点集合,集合所用花费最少的组将是游戏的赢家。
小可可和他的朋友邀请你一起参加这个游戏,由你来选择集合点,聪明的你能够完成这个任务,帮助小可可赢得游戏吗?
输入格式
第一行两个正整数 n 和 m,分别表示等待点的个数(等待点也从 1 到 n 进行编号)和获奖所需要完成集合的次数。
随后 n−1 行,每行两个正整数 a,b,表示编号为 a 和编号为 b 的等待点之间有一条路。
随后 m 行,每行用三个正整数 x,y,z,表示某次集合前小可可、小可可的朋友以及你所在等待点的编号。
输出格式
输出共 m 行,每行两个用空格隔开的整数 p,c。其中第 i 行表示第 i 次集合点选择在编号为 p 的等待点,集合总共的花费是 c 个游戏币。
答案必然在某两个的LCA上
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=5e5+10;
int nxt[N<<1],head[N],go[N<<1],tot;
inline int read(){
int x=0; char c=getchar();
while(c<'0'||c>'9')c=getchar();
while('0'<=c&&c<='9'){ x=(x<<1)+(x<<3)+c-'0'; c=getchar(); }
return x;
}
inline void add(int u,int v){
nxt[++tot]=head[u],head[u]=tot,go[tot]=v;
nxt[++tot]=head[v],head[v]=tot,go[tot]=u;
}
int f[N][20],dep[N];
void Deal(int u,int fa){
f[u][0]=fa,dep[u]=dep[fa]+1;
for(int i=1;i<=19;i++)f[u][i]=f[f[u][i-1]][i-1];
for(int i=head[u];i;i=nxt[i]){
int v=go[i];
if(v==fa)continue;
Deal(v,u);
}
}
inline int LCA(int x,int y){
if(dep[x]<dep[y])swap(x,y);
for(int i=19;i>=0;i--)if(dep[f[x][i]]>=dep[y])x=f[x][i];
if(x==y)return x;
for(int i=19;i>=0;i--)
if(f[x][i]!=f[y][i]){
x=f[x][i];
y=f[y][i];
}
return f[x][0];
}
int n,m;
inline int dis(int x,int y){
return dep[x]+dep[y]-2*dep[LCA(x,y)];
}
signed main(){
cin>>n>>m;
for(int i=1;i<n;i++)add(read(),read());
Deal(1,1);
int x,y,z;
while(m--){
x=read(),y=read(),z=read();
int ans=1e9,w,mzx;
int a1=LCA(x,y),a2=LCA(x,z),a3=LCA(z,y);
mzx=dep[x]+dep[y]-2*dep[a1]+dis(z,a1);
if(ans>mzx)ans=mzx,w=a1;
mzx=dep[x]+dep[z]-2*dep[a2]+dis(y,a2);
if(ans>mzx)ans=mzx,w=a2;
mzx=dep[z]+dep[y]-2*dep[a3]+dis(x,a3);
if(ans>mzx)ans=mzx,w=a3;
printf("%d %d
",w,ans);
}
}