BZOJ1787 [Ahoi2008]Meet 紧急集合 LCA

欢迎访问~原文出处——博客园-zhouzhendong

去博客园看该题解


题目传送门 - BZOJ1787


题意概括

  有一棵节点为n个(n≤500000)的树。接下来m次询问(m≤500000),每次给出3个点 a,b,c ,现在让你求一个点 p ,使得 dis(p,a) + dis(p,b) + dis(p,c) 最小。

  输出 p 和 dis(p,a) + dis(p,b) + dis(p,c)。


 

题解

  分别求3个LCA。

  学习LCA  -> 传送门

  有两个一样的,那么另外一个就是答案。


 

代码

#pragma comment(linker,"/STACK:1024000000,1024000000")
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <cstdlib>
using namespace std;
const int N=500000+5,M=N*2;
struct Gragh{
	int cnt,y[M],nxt[M],fst[N];
	void set(){
		cnt=0;
		memset(fst,0,sizeof fst);
	}
	void add(int a,int b){
		y[++cnt]=b,nxt[cnt]=fst[a],fst[a]=cnt;
	}
}g;
int n,m,depth[N],anst[N][20];
void dfs(int prep,int rt){
	depth[rt]=depth[anst[rt][0]=prep]+1;
	for (int i=1;i<20;i++)
		anst[rt][i]=anst[anst[rt][i-1]][i-1];
	for (int i=g.fst[rt];i;i=g.nxt[i])
		if (g.y[i]!=prep)
			dfs(rt,g.y[i]);
}
int LCA(int a,int b){
	if (depth[a]>depth[b])
		swap(a,b);
	for (int j=depth[b]-depth[a],i=0;j>0;j>>=1,i++)
		if (j&1)
			b=anst[b][i];
	if (a==b)
		return a;
	for (int i=19;i>=0;i--)
		if (anst[a][i]!=anst[b][i])
			a=anst[a][i],b=anst[b][i];
	return anst[a][0];
}
int main(){
	scanf("%d%d",&n,&m);
	g.set();
	for (int i=1,a,b;i<n;i++){
		scanf("%d%d",&a,&b);
		g.add(a,b);
		g.add(b,a);
	}
	depth[0]=-1;
	memset(anst,0,sizeof anst);
	dfs(0,1);
	for (int i=1,a,b,c,ans,pos;i<=m;i++){
		scanf("%d%d%d",&a,&b,&c);
		int p1=LCA(a,b),p2=LCA(a,c),p3=LCA(b,c);
		if (p1==p2)
			pos=p3;
		else if (p1==p3)
			pos=p2;
		else
			pos=p1;
		int q1=LCA(pos,a),q2=LCA(pos,b),q3=LCA(pos,c);
		ans=depth[a]+depth[b]+depth[c]+depth[pos]*3-depth[q1]*2-depth[q2]*2-depth[q3]*2;
		printf("%d %d\n",pos,ans);
	}
	return 0;
}

  

posted @ 2017-08-30 21:47  zzd233  阅读(239)  评论(0编辑  收藏  举报