Description
Input
Output
Sample Input
6 4
1 2
2 3
2 4
4 5
5 6
4 5 6
6 3 1
2 4 4
6 6 6
1 2
2 3
2 4
4 5
5 6
4 5 6
6 3 1
2 4 4
6 6 6
Sample Output
5 2
2 5
4 1
6 0
HINT
思路:很清晰,答案显然是三点中两点lca之间的一个
本题的题解也比较完善了,p点应该取的是3对点lca之间不同的那一个,这里详细证明一下
假设lca(x,y)=p1 lca(x,z)=p1 lca(y,z)=p2
那么显然,p2到y,z的距离小于p1到y,z的距离。
假设p1到p2之间的距离为l
如果点选了p1,那么总长度应该是len(x,p1)+(len(y,p2)+l)+(len(z,p2)+l)
如果点选了p2,那么总长度应该是(len(x,p1)+l)+len(y,p2)+len(z,p2);
因为树上的话两点路径唯一,所以x到p2只能通过p1同理y,z到达p1必须通过p2
显然得到p2为最优解
lca过程不再赘述,别的题解上应该都有详细分析。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
int poin[1000001],next[1000001],h[6000001];
int f[600001][30],dep[600001],cnt;
int n,m,s;
inline void add(int x,int y)
{
poin[++cnt]=y;next[cnt]=h[x];h[x]=cnt;
}
inline void dfs(int poi,int fa,int depth)
{
dep[poi]=depth;
f[poi][0]=fa;
for (int i=h[poi];i;i=next[i])
if (poin[i]!=fa) dfs(poin[i],poi,depth+1);
}
inline int up(int x,int de)
{
while (dep[x]!=de)
{
int tmp=0;
while (dep[f[x][tmp]]>=de) tmp++;
x=f[x][tmp-1];
}
return x;
}
inline int get(int x,int y)
{
if (dep[x]>dep[y]) x=up(x,dep[y]);else if (dep[y]>dep[x]) y=up(y,dep[x]);
if(x==y) return x;
while (1)
{
int tmp=0;
if (f[x][tmp]==f[y][tmp]) return f[x][tmp];
while (f[x][tmp]!=f[y][tmp]) tmp++;
tmp--;
x=f[x][tmp];y=f[y][tmp];
}
}
inline int cal(int x,int y)
{
int t=get(x,y);
return abs(dep[t]-dep[x])+abs(dep[t]-dep[y]);
}
inline void doit(int x,int y,int z)
{
int p1=get(x,y),p2=get(y,z),p3=get(x,z),p;
if (p1==p2) p=p3;
if (p1==p3) p=p2;
if (p2==p3) p=p1;
int ans=cal(p,x)+cal(p,y)+cal(p,z);
printf("%d %d\n",p,ans);
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n-1;i++)
{
int x,y;
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
dfs(1,-1,1);
for (int j=1;j<=log2(n);j++)
for (int i=1;i<=n;i++)
{
int tmp=f[f[i][j-1]][j-1];
if (f[i][j-1])
f[i][j]=tmp;
}
for (int i=1;i<=m;i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
doit(x,y,z);
}
}