题意
给你一棵树,然后给三个点 (a,b,c),求其中两个点到另一个点的最短路径的重合部分的最长值
思路
首先暴力一下是哪两个点到另一个点,取其中的最大值
只要知道了三个点两两的公共祖先和三个点的公共祖先,剩下的就推一下重合部分的长度和位置的关系就行了
当然从其他大神的题解中,我发现了更简单的解法:
求出三个lca,并取深度最大的那个,就是我们要的三岔路口K,然后分别求出K到a,b,c三点的路径长度,取最大值+1就是答案。
代码
#include <bits/stdc++.h>
using namespace std;
const int MAXN=1e5+10;
int n,q,st[MAXN][21],a[4],dep[MAXN];
int head[MAXN],to[MAXN*2],nxt[MAXN*2],tot=1;
void add(int u,int v){
to[++tot]=v;nxt[tot]=head[u];head[u]=tot;
}
void dfs(int u,int fa){
dep[u]=dep[fa]+1;st[u][0]=fa;
for(int i=head[u];i;i=nxt[i])
if(to[i]!=fa) dfs(to[i],u);
}
int lca(int x,int y){
if(dep[x]>dep[y]) swap(x,y);
for(int i=20;i>=0;i--) if(dep[st[y][i]]>=dep[x]) y=st[y][i];
if(x==y) return x;
for(int i=20;i>=0;i--) if(st[x][i]!=st[y][i]) x=st[x][i],y=st[y][i];
return st[x][0];
}
int calc(int a,int b,int c){
int ab=lca(a,b),bc=lca(b,c),ac=lca(a,c),abc=lca(ab,c);
if(a==b) return dep[a]+dep[c]-2*dep[ac]+1;
if(bc==ac) return abs(dep[abc]-dep[c])+1+abs(dep[abc]-dep[ab]);
else if(bc!=ab&&ac==ab) return abs(dep[bc]-dep[c])+1;
else if(ac!=ab&&bc==ab) return abs(dep[ac]-dep[c])+1;
}
int main(){
scanf("%d%d",&n,&q);
for(int i=2,x;i<=n;i++){
scanf("%d",&x);
add(i,x);add(x,i);
}
dfs(1,1);
for(int j=1;j<=20;j++)
for(int i=1;i<=n;i++)
st[i][j]=st[st[i][j-1]][j-1];
while(q--){
scanf("%d%d%d",&a[1],&a[2],&a[3]);
int ans=0;
sort(a+1,a+3+1);
do{
int temp=calc(a[1],a[2],a[3]);
ans=max(ans,temp);
}while(next_permutation(a+1,a+3+1));
printf("%d
",ans);
}
return 0;
}