P4281 [AHOI2008]紧急集合 / 聚会

P4281 [AHOI2008]紧急集合 / 聚会

给定一棵树, 边权为 (1), 多次询问, 给出三个点 (a, b, c) 求三个点的中点和到中点的总距离
定义中点为 (x) 满足 (dis(a, x) + dis(b, x) + dis(c, x)) 最小


调试日志: (jump) 数组开小, 只开了 (19) ...


Solution

首先是树上两点距离:$$dis(x, y) = dep[x] + dep[y] - 2 * dep[LCA(x, y)]$$
根到 (LCA) 的距离计算了两遍, 减去, 即可得到上式

回到这题, 发现三点两两 (LCA) 中总有两个一样
为什么呢? 设 (x = LCA(a, b))(LCA(x, c)) 即为重合的点, 因为此点是 (x) 的祖先, 必然是 (a, b) 的祖先
然后 找规律 可得集合点一定是三个 (LCA) 中深度最大的那个
我呸
可以发现, 深度大的那个 (LCA) 掌管着更深的两个点, 用反证法, 若是将集合点上移 (d) 对于一个点 (-d) 对于另外深度大两个点一共 (+2d) , 即总距离 (+d) , 不优
而若是将集合点下移 (d) , 那么此点将不在三点的公共路线上, 必然不优
综上可得: 集合点在三个 (LCA) 中深度大的点

然后套距离公式, 发现化简得如下式子$$min dis(a, b, c) = dep[a] + dep[b] + dep[c] - dep[lca1] - dep[lca2] - dep[lca3]$$

Code

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
#define LL long long
using namespace std;
int RD(){
    int out = 0,flag = 1;char c = getchar();
    while(c < '0' || c >'9'){if(c == '-')flag = -1;c = getchar();}
    while(c >= '0' && c <= '9'){out = out * 10 + c - '0';c = getchar();}
    return flag * out;
    }
const int maxn = 1000019,INF = 1e9 + 19;
int head[maxn],nume = 1;
struct Node{
    int v,dis,nxt;
    }E[maxn << 3];
void add(int u,int v,int dis){
    E[++nume].nxt = head[u];
    E[nume].v = v;
    E[nume].dis = dis;
    head[u] = nume;
    }
int num, na;
int dep[maxn], jump[maxn][25];
void dfs(int id, int F){//处理出深度用于计算距离
	for(int i = head[id];i;i = E[i].nxt){
		int v = E[i].v;
		if(v == F)continue;
		dep[v] = dep[id] + 1;
		jump[v][0] = id;
		dfs(v, id);
		}
	}
void get_jump(){
	for(int i = 1;i <= 19;i++){
		for(int j = 1;j <= num;j++){
			jump[j][i] = jump[jump[j][i - 1]][i - 1];
			}
		}
	}
int LCA(int x, int y){
	if(dep[x] < dep[y])swap(x, y);
	for(int i = 19;i >= 0;i--)if(dep[jump[x][i]] >= dep[y])x = jump[x][i];
	if(x == y)return x;
	for(int i = 19;i >= 0;i--){
		if(jump[x][i] != jump[y][i])x = jump[x][i], y = jump[y][i];
		}
	return jump[x][0];
	}
int main(){
	num = RD(), na = RD();
	for(int i = 1;i <= num - 1;i++){
		int u = RD(), v = RD();
		add(u, v, 1), add(v, u, 1);
		}
	dep[1] = 1;
	dfs(1, -1), get_jump();
	while(na--){
		int a = RD(), b = RD(), c = RD();
		int lca1 = LCA(a, b), lca2 = LCA(a, c), lca3 = LCA(b, c);
		int ans = dep[lca1] > dep[lca2] ? lca1 : lca2;
		ans = dep[lca3] > dep[ans] ? lca3 : ans;
		int dis = dep[a] + dep[b] + dep[c] - dep[lca1] - dep[lca2] - dep[lca3];
		printf("%d %d
", ans, dis);
		}
	return 0;
	}
原文地址:https://www.cnblogs.com/Tony-Double-Sky/p/9720860.html