[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;
}

浙公网安备 33010602011771号