传送门
// 题意 : 求给定树上的三点的组成的任意两条路径的重合度最大.
// 思路: 可以容易知道一个点到其他两个点时, 必定会经过哪两个点的LCA, 所以枚举出这三个点的三个LCA, 取深度最深的那个, 然后取三点中到那个深度最深的那个点的距离最大的值 .
AC Code
/** @Cain*/
const int maxn=1e5+5;
int up[maxn][25];
int deep[maxn],dis[maxn];
int head[maxn];
int n,m,cnt;
struct node
{
int to,next,w;
}s[maxn*2];
void init()
{
Fill(head,-1); Fill(dis,0);
Fill(up,0); Fill(deep,0);
cnt = 0;
}
void add(int u,int v,int w)
{
s[cnt].to = v;
s[cnt].w = w;
s[cnt].next = head[u];
head[u] = cnt++;
}
void dfs(int u,int fa,int dep)
{
deep[u] = dep + 1;
for(int i=1;i<20;i++){
up[u][i] = up[up[u][i-1]][i-1];
}
for(int i=head[u]; ~i ;i=s[i].next){
int v = s[i].to;
if(v == fa) continue;
dis[v] = dis[u] + s[i].w;
up[v][0] = u;
dfs(v,u,dep+1);
}
}
int LCA_BZ(int u,int v)
{
if(deep[u] < deep[v]) swap(u,v);
int k = deep[u] - deep[v];
for(int i=0;i<20;i++){ // 等价于kth(u,k);
if((1<<i) & k)
u = up[u][i]; //写进来好一点.
}
if(u!=v){
for(int i=19;i>=0;i--){
if(up[u][i] != up[v][i]){
u = up[u][i];
v = up[v][i];
}
}
u = up[u][0];
}
return u;
}
void solve()
{
scanf("%d%d",&n,&m);
init();
for(int i=2;i<=n;i++){
int v;
scanf("%d",&v);
add(i,v,1);
add(v,i,1);
}
up[1][0] = 1; //那根节点的父亲设为他自己.
dfs(1,-1,0);
while(m--){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
int d1 = LCA_BZ(a,b);
int d2 = LCA_BZ(a,c);
int d3 = LCA_BZ(b,c);
int maxx = d1;
if(dis[d2] > dis[maxx] ) maxx = d2;
if(dis[d3] > dis[maxx] ) maxx = d3;
d1 = dis[a] + dis[maxx] - 2 * dis[LCA_BZ(a,maxx)];
d2 = dis[b] + dis[maxx] - 2 * dis[LCA_BZ(b,maxx)];
d3 = dis[c] + dis[maxx] - 2 * dis[LCA_BZ(c,maxx)];
int res = max(d1,max(d2,d3));
printf("%d\n",res+1);
}
}