题解
D - Misha, Grisha and Underground
题目:一棵树,给三个节点,求它们间一个点到其余两个点路径的公共路径上经过节点数的最大值。
如果这三个整数构成了这样的两个路径,a到b,和,c到b 这两个路径。定义a到b的距离是lab,其他类推。那么这两个路径的交点个数是 ( lab + lbc - lac ) / 2 + 1那么用倍增在线LCA求任意两个节点的最短路径的距离,然后枚举a,b,c分别做为交点的路径情况的最大值。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <vector>
using namespace std;
typedef long long ll;
const int N = 1e5+7;
vector<int> son[N];
int depth[N],fa[N][21],in[N],a,b;
// depth[i] -> i 节点的深度
// fa[i][j] -> i 节点向上移动2^j个节点后的祖先
// fa[i][0] -> i 向上移动1个节点后的祖先,即父节点
// in[i] i节点的入度,用来找树根用的。
// a b 为读边用的。
int n, m;
void buildtree()
{
for(int i=2; i<=n; i++)
{
a=i;
scanf("%d",&b);
son[a].push_back(b);
son[b].push_back(a);
}
}
void dfs(int rt,int prev)
{
depth[rt]=depth[prev]+1;
fa[rt][0]=prev;
for(int i=1; i<20; i++)
{
fa[rt][i]=fa[fa[rt][i-1]][i-1];
}
for(int i=0; i<son[rt].size(); i++)
{
if(son[rt][i]==prev)
continue;
dfs(son[rt][i],rt);
}
}
int LCA(int x,int y)
{
if(depth[x]<depth[y]) swap(x,y);
for(int i=19; i>=0; i--)
{
if(depth[x]-(1<<i)>=depth[y])
{
x=fa[x][i];
}
}
if(x==y) return x;
for(int i=19; i>=0; i--)
{
if(fa[x][i]!=fa[y][i])
{
x=fa[x][i];
y=fa[y][i];
}
}
return fa[x][0];
}
int dist(int a,int b)
{
int u=LCA(a,b);
int L=depth[a]+depth[b]-2*depth[u];
return L;
}
int main()
{
scanf("%d",&n);
scanf("%d",&m);
buildtree();
depth[0]=-1;
int rt=1;// root
dfs(rt,rt);
int c;
for(int i=1; i<=m; i++)
{
scanf("%d %d %d",&a,&b,&c);
int ans=0;
int lab,lbc,lac,l1,l2,l3;
lac=dist(a,c);
lab=dist(a,b);
lbc=dist(b,c);
l1=(lab+lbc-lac)/2;
l2=(lab+lac-lbc)/2;
l3=(lac+lbc-lab)/2;
ans=max(l1,max(l2,l3));
printf("%d\n",ans+1);
}
return 0;
}