[bzoj1787][Ahoi2008]Meet 紧急集合
题目大意:给你一棵树,和$3$个节点,要你找到树上的一个点,使得三个点到这个点的距离和最小,并输出个距离
题解:令三个点为$a,b,c$,$i,j$两点的$lca$为$lca_{i,j}$,第$i$个点的深度为$depth_i$,$i,j$两点之间的距离为$d_{i,j}$。所以会发现$lca_{a,b},lca_{b,c},lca_{a,c}$中至少有两个是相同的。
假设$lca_{a,b}==lca_{a,c}$:
$\therefore lca_{a,b,c}==lca_{a,b}==lca_{a,c}$
$$
\begin{align*}
ans&=d_{b,c}+d_{lca_{b,c},a}\\
&=d_{b,lca_{b,c}}+d_{lca_{b,c},c}+d_{lca_{b,c},lca_{a,b,c}}+d_{lca_{a,b,c},a}\\
&=depth_b+depth_c-2 \cdot depth_{lca_{b,c}}+depth_{lca_{b,c}}+depth_a-2\cdot depth_{lca_{a,b}}\\
&=depth_a+depth_b+depth_c+depth_{lca_{b,c}}-2\cdot depth_{lca_{a,b}}
\end{align*}
$$
再常规化:
$$
ans=depth_a+depth_b+depth_c+depth+\max\{depth_{lca_{a,b}},depth_{lca_{a,c}},depth_{lca_{b,c}}\}-2\cdot\min\{depth_{lca_{a,b}},depth_{lca_{a,c}},depth_{lca_{b,c}}\}
$$
卡点:倍增求$LCA$时$for$循环未循环到$1$
C++ Code:
#include <cstdio> #define maxn 500010 #define lb(x) (x & -x) using namespace std; int n, m; inline void swap(int &a, int &b) {a ^= b ^= a ^= b;} int head[maxn], cnt; struct Edge { int to, nxt; } e[maxn << 1]; void addE(int a, int b) { e[++cnt] = (Edge) {b, head[a]}; head[a] = cnt; } int dad[maxn][19], dep[maxn]; void dfs(int rt) { for (int i = head[rt]; i; i = e[i].nxt) { int v = e[i].to; if (v != dad[rt][0]) { dep[v] = dep[rt] + 1; dad[v][0] = rt; dfs(v); } } } void init() { for (int i = 1; i < 19; i++) { for (int j = 1; j <= n; j++) { dad[j][i] = dad[dad[j][i - 1]][i - 1]; } } } int LCA(int x, int y) { if (x == y) return x; if (dep[x] < dep[y]) swap(x, y); for (int t = dep[x] - dep[y]; t; t &= t - 1) x = dad[x][__builtin_ctz(lb(t))]; if (x == y) return x; for (int i = 18; ~i; i--) if (dad[x][i] != dad[y][i]) x = dad[x][i], y = dad[y][i]; return dad[x][0]; } int main() { scanf("%d%d", &n, &m); for (int i = 1; i < n; i++) { int a, b; scanf("%d%d", &a, &b); addE(a, b); addE(b, a); } dep[1] = 1; dfs(1); init(); while (m --> 0) { int a, b, c, ans = 0; scanf("%d%d%d", &a, &b, &c); int lca_ab = LCA(a, b), lca_ac = LCA(a, c), lca_bc = LCA(b, c); printf("%d ", lca_ab ^ lca_ac ^ lca_bc); if (lca_ab == lca_ac) ans = dep[b] + dep[c] - dep[lca_bc] + dep[a] - (dep[lca_ac] << 1); else { if (lca_bc == lca_ab) ans = dep[a] + dep[c] - dep[lca_ac] + dep[b] - (dep[lca_ab] << 1); else ans = dep[a] + dep[b] - dep[lca_ab] + dep[c] - (dep[lca_ac] << 1); } printf("%d\n", ans); } return 0; }