题意:已知一棵树节点数n,n-1 条边组成。Q次询问,现在从中选取3个点a,b,c。 以一个点为顶点,另外两个点为起始点走最短路。要求得3种组合中,公共点数量最多的情况。


思路:枚举3种情况。

节点A,B之间最短路距离是:dis(A,B)=dep[A]-dep[LCA(A,B)]+dep[B]-dep[LCA(A,B)];

两节点A,B到节点C的    公共点个数=  (  dis(A,C)+dis(B,C)-dis(A,B)  )/2  + 1

接下来主要是模板一套就好了


数据分析:2 ≤ n ≤ 1051 ≤ q ≤ 105


复杂度分析:O(nlogn + q * logn)


CODE:(详见 算法学习之   LCA模板)传送门

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int maxn=1e5+50;
const int maxlog=20;
int n,q,root=1;
int par[maxlog][maxn];
int dep[maxn];
vector <int> G[maxn];

void dfs(int v,int p,int d)
{
    par[0][v]=p;
    dep[v]=d;
    for(int i=0;i<G[v].size();i++)
        if(G[v][i]!=p)  dfs(G[v][i],v,d+1);
    return ;
}

void init()
{
    dfs(root,-1,0);
    for(int k=0;k<=maxlog-2;k++)
        for(int v=1;v<=n;v++)
            par[k+1][v]= par[k][v]<0 ? -1:par[k][par[k][v]];

    return ;
}

int lca(int u,int v)
{
    if(dep[u]>dep[v])   swap(u,v);
    for(int k=0;k<maxlog;k++)
        if((dep[v]-dep[u])>>k & 1)
            v=par[k][v];
    if(u==v)    return u;
    for(int k=maxlog-1;k>=0;k--)
        if(par[k][u]!=par[k][v])    u=par[k][u],v=par[k][v];
    return par[0][u];
}

int dis(int u,int v){return dep[u]+dep[v]-dep[lca(u,v)]*2;}

int cul(int t, int s ,int f){return (  dis(s,f)+dis(t,f)-dis(s,t)  )/2;}

int main(void)
{
    cin >> n >> q;
    for(int i=2;i<=n;i++)
    {
        int t;
        scanf("%d",&t);
        G[i].push_back(t);
        G[t].push_back(i);
    }
    init();
    while(q--)
    {

        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        printf("%d\n",max(  cul(a,b,c),max(cul(a,c,b) , cul(b,c,a)) )+1  );
    }
    return 0;
}