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

 





posted @ 2018-08-21 18:38  Memory_of_winter  阅读(142)  评论(0编辑  收藏  举报