CodeForces 832D. Misha, Grisha and Underground LCA

传送门

题意

给你一棵树,然后给三个点 \(a,b,c\),求其中两个点到另一个点的最短路径的重合部分的最长值

思路

首先暴力一下是哪两个点到另一个点,取其中的最大值
只要知道了三个点两两的公共祖先和三个点的公共祖先,剩下的就推一下重合部分的长度和位置的关系就行了
当然从其他大神的题解中,我发现了更简单的解法:
求出三个lca,并取深度最大的那个,就是我们要的三岔路口K,然后分别求出K到a,b,c三点的路径长度,取最大值+1就是答案。

代码

#include <bits/stdc++.h>
using namespace std;
const int MAXN=1e5+10;
int n,q,st[MAXN][21],a[4],dep[MAXN];
int head[MAXN],to[MAXN*2],nxt[MAXN*2],tot=1;

void add(int u,int v){
	to[++tot]=v;nxt[tot]=head[u];head[u]=tot;
}

void dfs(int u,int fa){
	dep[u]=dep[fa]+1;st[u][0]=fa;
	for(int i=head[u];i;i=nxt[i])
		if(to[i]!=fa) dfs(to[i],u);
}

int lca(int x,int y){
	if(dep[x]>dep[y]) swap(x,y);
	for(int i=20;i>=0;i--) if(dep[st[y][i]]>=dep[x]) y=st[y][i];
	if(x==y) return x;
	for(int i=20;i>=0;i--) if(st[x][i]!=st[y][i]) x=st[x][i],y=st[y][i];
	return st[x][0];
}

int calc(int a,int b,int c){
	int ab=lca(a,b),bc=lca(b,c),ac=lca(a,c),abc=lca(ab,c);
	if(a==b) return dep[a]+dep[c]-2*dep[ac]+1;
	if(bc==ac) return abs(dep[abc]-dep[c])+1+abs(dep[abc]-dep[ab]);
	else if(bc!=ab&&ac==ab) return abs(dep[bc]-dep[c])+1;
	else if(ac!=ab&&bc==ab) return abs(dep[ac]-dep[c])+1;
}

int main(){
	scanf("%d%d",&n,&q);
	for(int i=2,x;i<=n;i++){
		scanf("%d",&x);
		add(i,x);add(x,i);
	}
	dfs(1,1);
	for(int j=1;j<=20;j++)
		for(int i=1;i<=n;i++)
			st[i][j]=st[st[i][j-1]][j-1];
	while(q--){
		scanf("%d%d%d",&a[1],&a[2],&a[3]);
		int ans=0;
		sort(a+1,a+3+1);
		do{
			int temp=calc(a[1],a[2],a[3]);
			ans=max(ans,temp);
			
		}while(next_permutation(a+1,a+3+1));
		printf("%d\n",ans);
	}
	return 0;
}
posted @ 2020-01-08 02:16  BakaCirno  阅读(170)  评论(0编辑  收藏  举报