博客园 首页 私信博主 显示目录 隐藏目录 管理 动画

题解 [BZOJ1832][AHOI2008] 聚会

题面

解析

首先对于其中的两个点\(x,y\)最近的点显然就是他们的\(lca\)(我们把它设为\(p1\)),

然后考虑第三个点\(z\)\(p1\)\(lca,p2\).

有以下几种情况:

  1. \(dep[p1]>=dep[p2]\)(也就是\(p2\)\(p1\)上面或\(p1=p2\)),这时候答案显然就是\(p1\).

  2. \(dep[p1]<dep[p2]\),这时候我们求出\(p3=lca(x,z),p4=lca(y,z)\)

    1. \(dep[p3]>dep[p4]\),这时候\(p3\)显然更优(画下图或者\(yy\)一下就能理解)
    2. \(dep[p4]>dep[p3]\),同理,就是反过来...
    3. \(p3=p4\),随便选一个...

最后用深度算距离就行啦.

code(似乎有点卡常):

#include <iostream>
#include <cstdio>
#include <cstring>
#define fre(x) freopen(x".in","r",stdin),freopen(x".out","w",stdout)
using namespace std;

inline int read(){
	int sum=0,f=1;char ch=getchar();
	while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0' && ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
	return f*sum;
}

const int N=500005;
struct edge{int to,next;}e[N<<1];
int n,m;
int head[N],cnt;
int fa[N][20],dep[N];

inline void add(int x,int y){
	e[++cnt]=(edge){head[x],y};head[x]=cnt;
}

inline void dfs(int x,int f){
	fa[x][0]=f;dep[x]=dep[f]+1;
	for(int i=1;i<20;i++) fa[x][i]=fa[fa[x][i-1]][i-1];
	for(int i=head[x];i;i=e[i].to){
		int k=e[i].next;if(k==f) continue;
		dfs(k,x);
	}
}

inline int lca(int x,int y){
	if(dep[x]<dep[y]) swap(x,y);
	for(int i=19;i>=0;i--){
		if(dep[fa[x][i]]>=dep[y]) x=fa[x][i];
	}
	if(x==y) return x;
	for(int i=19;i>=0;i--){
		if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
	}
	return fa[x][0];
}

int main(){
	n=read();m=read();
	for(int i=1;i<n;i++){int x=read(),y=read();add(x,y);add(y,x);}
	dfs(1,0);
	for(int i=1;i<=m;i++){
		int x=read(),y=read(),z=read();
		int p1=lca(x,y),p2=lca(p1,z);
		if(dep[p2]<dep[p1]){
			printf("%d %d\n",p1,dep[x]-dep[p1]+dep[y]-dep[p1]+dep[p1]-dep[p2]+dep[z]-dep[p2]);
		}
		else{
			int p3=lca(x,z),p4=lca(y,z);
			if(dep[p3]>=dep[p4])
				printf("%d %d\n",p3,dep[x]-dep[p3]+dep[z]-dep[p3]+dep[y]-dep[p1]+dep[p3]-dep[p1]);
			else
				printf("%d %d\n",p4,dep[y]-dep[p4]+dep[z]-dep[p4]+dep[x]-dep[p1]+dep[p4]-dep[p1]);
		}
	}
	return 0;
}

posted @ 2019-08-26 22:05  Hastin  阅读(156)  评论(0编辑  收藏  举报