P10952 聚会 题解

题目链接

题目大意

对于一棵树,求出一个点对于给定的三个点(以下简称 \(x\)\(y\)\(z\) 且可以重复)距离最短。

题解

对于点的距离,不难想到 LCA 处理。而对于本题,则有两种情况。

第一问

  1. 三点中有一为另外两个点的祖先时,所求目标点(以下简称 \(v\) )的深度(简称 \(d_v\) )一定在三点深度之间。

  2. 三点共同 LCA 为另一点时,\(v\) 即为三点的 LCA 。

这两种情况包含了所有形式,所以我们可以通过求出 \(\operatorname {LCA(x,y)}\)\(\operatorname {LCA(x,z)}\)\(\operatorname {LCA(y,z)}\) 后选取最深的点,即所求的 \(v\)。对于此结论,第二种情况自然不用多说,而第一种情况中显然 \(d_x \le d_v \le d_y\)\(d_x \le d_y \le d_z\)),最深的点可以使 \(y\)\(z\)\(v\) 的距离最短,即 \(y\)\(z\) 不会走相同的边。

第二问

设点 \(x\) 到根节点的距离为 \(dis_x\),由树的性质得答案为 \(dis_x+dis_y+dis_z-dis_{\operatorname{LCA(x,y)}}-dis_{\operatorname{LCA(x,z)}}-dis_{\operatorname{LCA(y,z)}}\)

代码

#include <bits/stdc++.h>
using namespace std;
#define mst(x, y) memset(x, y, sizeof(x))
#define pp pair<int, int>
#define fi first
#define se second
#define mp(x, y) make_pair(x, y)

int read(){int x = 0, f = 1;char c = getchar();while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();}while(c >= '0' && c <= '9'){x = 10*x+c-'0';c = getchar();}return f*x;}
void write(int x){if(x < 0){putchar('-');x = -x;}if(x > 9) write(x/10);putchar(x%10 | 0x30);return;}
const int N = 1000005, inf = 0x3f3f3f3f;

int n, q, hd[N], ver[N], nxt[N], idx, d[N], f[N][25], ans, mk[N];

void add(int x, int y){
	nxt[++idx] = hd[x];
	ver[idx] = y;
	hd[x] = idx;
}
void bfs(){
	queue <int> q;
	q.push(1);
	mk[1] = 1, d[1] = 1;
	while(q.size()){
		int t = q.front();
		q.pop();
		for(int i = hd[t];i;i = nxt[i]){
			int y = ver[i];
			if(mk[y]) continue;
			mk[y] = 1;
			d[y] = d[t]+1, f[y][0] = t;
			for(int j = 1;j <= 20;j++){
				f[y][j] = f[f[y][j-1]][j-1];
			}
			q.push(y);
		}
	}
}
int lca(int x, int y){
	if(d[x] < d[y]) swap(x, y);
	for(int i = 20;i >= 0;i--){
		if(d[f[x][i]] >= d[y]) x = f[x][i];
	}
	if(x == y) return x;
	for(int i = 20;i >= 0;i--){
		if(f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
	}
	return f[x][0];
}
void init(){
	n = read(), q = read();
	for(int i = 1;i < n;i++){
		int x = read(),y = read();
		add(x, y);add(y, x);
	}
	bfs();
}
void solve(){
	while(q--){
		int x = read(), y = read(), z = read();
		int l1 = lca(x, y), l2 = lca(y, z), l3 = lca(x, z);
		ans = d[x]+d[y]+d[z]-d[l1]-d[l2]-d[l3];
		if(d[l1] >= d[l2] && d[l1] >= d[l3]){
			write(l1);putchar(' ');write(ans);puts("");
		}
		else if(d[l2] >= d[l1] && d[l2] >= d[l3]){
			write(l2);putchar(' ');write(ans);puts("");

		}
		else if(d[l3] >= d[l2] && d[l3] >= d[l1]){
			write(l3);putchar(' ');write(ans);puts("");
		}
	}
}

int main(){
	init();
	solve();
	return 0; 
}

另外不要忘了双倍经验

posted @ 2024-12-25 20:56  Hirasawayuiii  阅读(37)  评论(2)    收藏  举报