LCA结论题 [AHOI2008]紧急集合 / 聚会(洛谷 P4281)

[AHOI2008]紧急集合 / 聚会

题目大意:

给你一颗n个结点的树,然后给你m个3元组(x,y,z);知道到一个点,这个点到x,y,z的距离和最小;


结论:x,y,z两两求lca,会发现有两个lca相等,不相等的那个lca就是最小距离点,根据这个点求最小距离和即可;

代码:

#include<bits/stdc++.h>
#define LL long long
#define pa pair<int,int>
#define ls k<<1
#define rs k<<1|1
#define inf 0x3f3f3f3f
using namespace std;
const int N=500100;
const int M=50100;
const LL mod=10007;
int n,m,head[N],cnt,lg[N],dep[N],fa[N][40];
struct Node{
	int to,nex;
}edge[N*2];
void add(int p,int q){edge[cnt].to=q,edge[cnt].nex=head[p],head[p]=cnt++;}
void dfs(int sn,int ft){
	dep[sn]=dep[ft]+1,fa[sn][0]=ft;
	for(int i=1;i<=lg[dep[sn]];i++) fa[sn][i]=fa[fa[sn][i-1]][i-1];
	for(int i=head[sn];~i;i=edge[i].nex){
		if(edge[i].to!=ft) dfs(edge[i].to,sn);
	}
}
int lca(int x,int y){
	if(dep[x]<dep[y]) swap(x,y);
	while(dep[x]>dep[y]) x=fa[x][lg[dep[x]-dep[y]]-1];
	if(x==y) return x;
	for(int k=lg[dep[x]]-1;k>=0;k--){
		if(fa[x][k]!=fa[y][k]){
			x=fa[x][k],y=fa[y][k];
		}
	}
	return fa[x][0];
}
int main(){
	memset(head,-1,sizeof(head));
	scanf("%d%d",&n,&m);
	for(int i=1;i<n;i++){
		int a,b;scanf("%d%d",&a,&b);
		add(a,b),add(b,a);
	}
	for(int i=1;i<=n;i++) lg[i]=lg[i-1]+(1<<lg[i-1]==i);
	dfs(1,0);
	for(int i=1;i<=m;i++){
		int x,y,z;scanf("%d%d%d",&x,&y,&z);
		int lcaxy=lca(x,y),lcaxz=lca(x,z),lcayz=lca(y,z);
		if(lcaxy==lcaxz){//lcayz 
			int lenyz=dep[y]+dep[z]-2*dep[lcayz];
			int lcayz_x=lca(lcayz,x);
			int lenyz_x=dep[x]+dep[lcayz]-2*dep[lcayz_x];
			printf("%d %d\n",lcayz,lenyz+lenyz_x);
		}
		else if(lcaxy==lcayz){//lcaxz
			int lenxz=dep[x]+dep[z]-2*dep[lcaxz];
			int lcaxz_y=lca(lcaxz,y);
			int lenxz_y=dep[y]+dep[lcaxz]-2*dep[lcaxz_y];
			printf("%d %d\n",lcaxz,lenxz+lenxz_y);
		}
		else{//lcaxy
			int lenxy=dep[x]+dep[y]-2*dep[lcaxy];	
			int lcaxy_z=lca(lcaxy,z);
			int lenxy_z=dep[z]+dep[lcaxy]-2*dep[lcaxy_z];
			printf("%d %d\n",lcaxy,lenxy+lenxy_z);
		}
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值