题目链接
http://codeforces.com/contest/832/problem/D
题目大意
给你一棵树, 以及q次询问,每次询问给你a,b, c三个节点
你可以将这三个节点任意(一一对应)定为s, f, t节点,
然后进行以下操作
从s–>f跑最短路,并标记路上的点
从t–>f跑最短路,统计刚刚标记的点的个数
(每次询问过后标记的点会被清空)
问你每次询问统计出的标记的点个数的最大值
思路
树上最短路我们可以用LCA(两个节点最近的公共祖先)进行计算
我们用ab表示lca(a, b), ac = lca(a, c), bc = lca(b, c)
这样必定会有两个值相等, 如下图所示
那么最大值必然在三条路之间
a–>ab
b–>ab
c–>ab
至于路径的计算,lca计算过程中有一个deep数组,用于记录节点所在深度,那么a–>ab距离 = deep[a] - deep[ab], 以此类推
代码
#include<bits/stdc++.h>
using namespace std;
const int M = 1e5 + 5;
vector<int>g[M];
int root;
int parent[20][M];
int dep[M];
int n, q;
void dfs(int v, int p, int d)
{
parent[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);
}
}
void init()
{
for(int k = 0; k < 20-1; ++ k)
{
for(int v = 1; v <= n; ++ v)
{
if(parent[k][v] < 0) parent[k+1][v] = -1;
else
{
parent[k+1][v] = parent[k][parent[k][v]];
}
}
}
}
int getlca(int u, int v)
{
if(dep[u] > dep[v]) swap(u, v);
int ans = 0;
for(int k = 0; k < 20; ++ k)
{
if((dep[v] - dep[u]) >> k & 1)
{
v = parent[k][v];
}
}
if(u != v)
{
for(int k = 20 - 1; k >= 0; -- k)
{
if(parent[k][u] != parent[k][v])
{
u = parent[k][u], v = parent[k][v];
}
}
u = parent[0][u];
return u;
}
return u;
}
int solve(int a, int b, int c, int ab, int ac)
{
int mx = 0;
mx = max(dep[a] - dep[ab] + 1, dep[b] - dep[ab] + 1);
mx = max(mx, dep[ab] - dep[ac] + dep[c] - dep[ac] + 1);
return mx;
}
int main()
{
scanf("%d%d", &n, &q);
for(int i=1; i<n; ++i)
{
int tmp;
scanf("%d", &tmp);
g[i+1].push_back(tmp);
g[tmp].push_back(i+1);
}
dfs(1, -1, 1);
init();
for(int i=0; i<q; ++i)
{
int a, b, c, ans = 0;
scanf("%d%d%d", &a, &b, &c);
int ab = getlca(a, b);
int bc = getlca(b, c);
int ac = getlca(a, c);
int mx = max(dep[ab], max(dep[bc], dep[ac]));
if(dep[ab] == mx)
{
ans = solve(a, b, c, ab, ac);
}
else if(dep[ac] == mx)
{
ans = solve(a, c, b, ac, ab);
}
else if(dep[bc] == mx)
{
ans = solve(b, c, a, bc, ab);
}
printf("%d\n", ans);
}
return 0;
}