CodeForces 832D Round #425 D Misha, Grisha and Underground :LCA求树上路径长度

本文介绍了一种解决树形结构中寻找特定三个点间路径重叠问题的算法。通过计算最低公共祖先(LCA)及路径长度,该算法能够找到路径重叠的最大值。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:给定一颗树(n<=1e5),并有q(<=1e5)次询问,每次询问指定三个点a,b,c:意思是有两个人任选两个点作为各自起点,剩下的那个点作为终点,两个人各自从起点走向终点,求路径上重合的点数,现在要求在这三个点可能的选择方式中,求重合点数的最大值。

题解:对于三个不同的点a,b,c,画出两两间的道路,他们必然呈现出三岔路的形状。科学点说就是:a,b,c三点里,必有一对点的lca在某两点的路径上,而三岔路口的地方就是所说的lca,这个画个图可以很容易的讨论证明。所以只需要求出三个lca,并取深度最大的那个,就是我们要的三岔路口K,然后分别求出K到a,b,c三点的路径长度,取最大值+1就是答案。

而当有两个点相同,第三个点不同的情况,可以得知:答案=唯一的路径长+1,和我们算法给出的答案是一致的。

当三个点都相同,答案也是一致的。

所以不需要讨论。

Code:

#include<bits/stdc++.h>
using namespace std;
#define MAXN 100005
int deep[MAXN];
vector<int> E[MAXN];
int father[MAXN][20];
int n,q;
void build (int nod,int fa,int depth){
	deep[nod] = depth;
	for (vector<int>::iterator it = E[nod].begin();it!=E[nod].end();it++){
		if ((*it)!=fa){
			father[*it][0] = nod;
			build (*it,nod,depth+1);
		}
	}
}
void initLCA(){
	for (int i=1;i<=19;i++){
		for (int j = 1;j<=n;j++){
			father[j][i] = father[father[j][i-1]][i-1];
		}
	}
}
int lca(int a,int b){
	if (deep[a]<deep[b]){
		swap(a,b);
	}
	for (int i=19;i>=0;i--){
		if (deep[father[a][i]]>=deep[b]){
			a = father[a][i];
		}
	}
	if (a==b){
		return a;
	}
	for (int i=19;i>=0;i--){
		if (father[a][i]!=father[b][i]){
			a = father[a][i];
			b = father[b][i];
		}
	}
	return father[a][0];
}
int length (int a,int b){
	int anc = lca (a,b);
	return (deep[a]+deep[b]-2*deep[anc]);
}
int main(){
	scanf("%d%d",&n,&q);
	for (int i=2;i<=n;i++){
		int temp;
		scanf("%d",&temp);
		E[temp].push_back(i);
		E[i].push_back(temp);
	}
	build (1,0,1);
	initLCA();
	while (q--){
		int a,b,c;
		scanf("%d%d%d",&a,&b,&c);
		int anc1 = lca(a,b);
		int anc2 = lca(a,c);
		int anc3 = lca(b,c);
		if (deep[anc2]>deep[anc1]){
			anc1 = anc2;
		}
		if (deep[anc3]>deep[anc1]){
			anc1 = anc3;
		}
		int ans1 = length(anc1,a);
		int ans2 = length(anc1,b);
		int ans3 = length(anc1,c);
		int ans = max (max ( ans1,ans2),ans3)+1;
		printf("%d\n",ans);
	}
	return 0;
}


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值