P4281 [AHOI2008]紧急集合 / 聚会

此题来到LCA较高等级运用。

这道题需要自己花一些树玩玩。

找到一些性质:

  1. 三个点的lca一定至少有两个是一样的;更多证明
  2. 集合点就是不相同的点;

同时还要会求树上距离

image

这里 \(x,y,z\) 是三个人,\(l\) 是重复lca,\(p\) 就是集合点。那么距离就是 \(dep(y)-dep(p)+dep(z)-dep(p)+dep(x)-dep(l)+dep(p)-dep(l)\) 简化一下就是 \(dep(x)+dep(y)+dep(z)-dep(l) * 2 + dep(p)\)

一般地就是 \(dep(x)+dep(y)+dep(z)-dep(lca_1)-dep(lca_2)-dep(lca_3)\)

因为重复项乘2 也就是两个相加罢了。

#include <bits/stdc++.h>
#define ll long long
inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0' && ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x*f;}
using namespace std;
const int maxn = 5 * 100002;
int n, m;
int f[maxn][20];
int dep[maxn];
vector<int>e[maxn];
void dfs(int u, int fa)
{
	f[u][0] = fa;
	dep[u] = dep[fa] + 1;
	for (int i = 1; i <= 19; i++) {
		f[u][i] = f[f[u][i - 1]][i - 1];
	}
	for (int i : e[u]) {
		if (i != fa) {
			dfs(i, u);
		}
	}
}
int lca(int u, int v)
{
	if (dep[u] <dep[v]) swap(u,v);
	for (int i = 19; i >= 0; i--) {
		if (dep[f[u][i]] >= dep[v]) {
			u = f[u][i];
		}
	}
	if (u == v) {
		return v;
	}
	for (int i = 19; i >= 0; i--) {
		if (f[u][i] != f[v][i]) {
			u = f[u][i];
			v = f[v][i];
		}
	}
	return f[u][0];
}
int main()
{
	n = read(), m =read();
	for (int i = 1, a, b; i < n; i++) {
		a = read(), b = read();
		e[a].push_back(b);
		e[b].push_back(a);
	}
	dfs(1, 0); // root?
	int a, b, c;
	for (int kase = 1; kase <= m; kase ++) {
		a = read(); b = read(); c = read();
		int l1 = lca(a, b);
		int l2 = lca(a, c);
		int l3 = lca(b, c);
		int ansl;
		if (l1 == l2) {
			ansl = l3;
		} else if (l2 == l3) {
			ansl = l1;
		} else {
			ansl = l2;
		}
		printf("%d %d\n", ansl, dep[a] + dep[b] + dep[c] - dep[l1] - dep[l2] - dep[l3]);
	}
    return 0;
}
posted @ 2023-01-26 10:25  Vegdie  阅读(24)  评论(0编辑  收藏  举报