CF832D Misha, Grisha and Underground |LCA

有一棵n个节点的树,一共q 次询问

每次询问给定3个点,求两条起点终点在这三个点上且不完全重合的路径的最多公共节点数


简单LCA求距离,令a为汇合点,那么答案就是(dis(a,b) + dis(a,c) - dis(b,c)) / 2 + 1,dis用lca求出,枚举a就好。

当然也可以一一讨论abc的位置关系,不过容易出错。

#include<queue>
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=2e5+10;
#define int long long
int Next[N*2],head[N],go[N*2],tot;
inline void add(int u,int v){
	Next[++tot]=head[u];head[u]=tot;go[tot]=v;
	Next[++tot]=head[v];head[v]=tot;go[tot]=u;
}
int n,q;
int d[N],f[N][21];
inline void pre(int u,int fa){
	d[u]=d[fa]+1;f[u][0]=fa;
	for(int i=1;i<=20;i++)f[u][i]=f[f[u][i-1]][i-1];
	for(int i=head[u];i;i=Next[i]){
		int v=go[i];
		if(v==fa)continue;
		pre(v,u);
	}
}
inline 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];
}
inline int dis(int a,int b){
	int c=LCA(a,b);
	return d[a]-d[c]+d[b]-d[c];
}
inline int ask(int a,int b,int c){
	return 1+(dis(a,b)+dis(a,c)-dis(b,c))>>1;
}
signed main(){
	cin>>n>>q;
	for(int i=2,x;i<=n;i++)
	scanf("%lld",&x),add(x,i);
	pre(1,0);
	int a,b,c;
	while(q--){
		scanf("%lld%lld%lld",&a,&b,&c);
		int ans=0;
		ans=max(ans,ask(a,b,c));
		ans=max(ans,ask(b,a,c));
		ans=max(ans,ask(c,b,a));
		printf("%lld\n",ans);
	}
}
posted @ 2019-10-30 16:38  白木偶君  阅读(142)  评论(0编辑  收藏  举报