題目鏈接
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;
}