题目大意:给你一棵树,有 q 个询问,每次给你三个数,其中一个当做终点,两个当做起点,问你这样两条路有多少公共节点。

 

思路:LCA,如果只问你 a,b 分别到 c 的两条路有多少个公共节点的话,就是( dis( a , c) + dis( b , c) - dis( a , b ) ) / 2 + 1 ;

所以我们知道求出每两个点之间的最短距离,(最长的 + 第二长 - 最短的)/ 2 + 1;

Codeforces Round #425 (Div. 2) -D - Misha, Grisha and Underground_最短距离Codeforces Round #425 (Div. 2) -D - Misha, Grisha and Underground_i++_02
#include<bits/stdc++.h>
#define pb push_back
using namespace std;
const int N=1e5+5;
int vs[2*N],dp[2*N][25],d[N],tot=0,f[N],w[3];
vector<int> e[N];
int n,q;
void dfs(int u,int p,int s)
{
    vs[++tot]=u;
    d[u]=s;
    f[u]=tot;
    for(int i=0;i<e[u].size();i++)
    {
        int to=e[u][i];
        if(to==p) continue;
        dfs(to,u,s+1);
        vs[++tot]=u;
    }
}
void work_rmq()
{
    for(int i=1;i<=tot;i++) dp[i][0]=vs[i];
    int up=log(tot)/log(2);
    for(int j=1;j<=up;j++)
    {
        int t=(1<<j)-1;
        for(int i=1;i+t<=tot;i++)
        {
            int a=dp[i][j-1],b=dp[i+(1<<(j-1))][j-1];
            if(d[a]<=d[b]) dp[i][j]=a;
            else dp[i][j]=b;
        }
    }
}
int get_lca(int u,int v)
{
    int l,r;
    if(f[u]<f[v]) l=f[u],r=f[v];
    else l=f[v],r=f[u];
    int j=log(r-l+1)/log(2);
    int a=dp[l][j],b=dp[r-(1<<j)+1][j];
    if(d[a]<d[b]) return a;
    else return b;
}
int get_dis(int u,int v)
{
    int f=get_lca(u,v);
    int res=fabs(d[u]-d[f])+fabs(d[v]-d[f]);
    return res;
}
int main()
{
    cin>>n>>q;
    for(int i=2;i<=n;i++)
    {
        int g; scanf("%d",&g);
        e[i].pb(g); e[g].pb(i);
    }
    dfs(1,-1,0);
    work_rmq();
    while(q--)
    {
        int ans[3];
        for(int i=0;i<=2;i++) scanf("%d",&w[i]);
        ans[0]=get_dis(w[0],w[1]);
        ans[1]=get_dis(w[0],w[2]);
        ans[2]=get_dis(w[1],w[2]);
        sort(ans,ans+3);
        printf("%d\n",(ans[2]+ans[1]-ans[0])/2+1);
    }
    return 0;
}
View Code