题意:已知一棵树节点数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 ≤ 105, 1 ≤ 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;
}