[bzoj1787][Ahoi2008]Meet 紧急集合

Posted memory-of-winter

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[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}$:

$ herefore lca_{a,b,c}==lca_{a,b}==lca_{a,c}$
$$
egin{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-2cdot depth_{lca_{a,b}}\
&=depth_a+depth_b+depth_c+depth_{lca_{b,c}}-2cdot 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}}-2cdotmin{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
", ans);
		
	}
	return 0;
}

 





以上是关于[bzoj1787][Ahoi2008]Meet 紧急集合的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ-1787: [Ahoi2008]Meet 紧急集合 (LCA)

BZOJ——1787: [Ahoi2008]Meet 紧急集合

[BZOJ1787] [Ahoi2008]Meet 紧急集合

bzoj1787: [Ahoi2008]Meet 紧急集合

bzoj1787[Ahoi2008]Meet 紧急集合

bzoj 1787: [Ahoi2008]Meet 紧急集合