【旅行】

Description



【题解】

 一道lca水题

随便画画图就可以看出ABC之间的关系只有三种

1.三个节点有共同的共祖先  

2.两个节点的公共祖先与另一个节点的公共祖先相同,且这个公共祖先是那两个节点公共祖先的祖先

3.两个节点的公共祖先与另一个节点的公共祖先相同,且这个这两个节点的公共祖先时两个节点其中一个(一条链的情况)


图比较丑随便看看

因此我们只需要对于三个点两两找lca 然后判断出是那种情况

剩下的就比较简单了 参照代码查看集合点和距离的做法

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <stack>
#include <vector>
#include <queue>
#include <map>
using namespace std;
int i,j,k,l,m,n,x,y,z,fa,fb,fc,q,num;
int f[200005][22],d[200005],first[200005];
struct info
  {
  	int ar,next;
  }tree[400005];
void add(int x,int y) {tree[++num]={y,first[x]};first[x]=num;}
void dfs(int u,int fat)
  {
  	int i,v;
  	for (i=first[u];i;i=tree[i].next)
  	  {
  	  	v=tree[i].ar;
  	  	if (v==fat) continue;
  	  	d[v]=d[u]+1;f[v][0]=u;
  	  	dfs(v,u);
	  }
  }
int lca(int u,int v)
  {
  	int i;
  	if (d[v]>d[u]) swap(u,v);
  	for (;d[u]>d[v];)
  	  {
  	  	for (i=0;d[f[u][i]]>=d[v];i++);i--;
  	  	u=f[u][i];
	  }
	for (;u!=v;)
	  {
	  	for (i=1;f[u][i]!=f[v][i];i++);i--;
	  	u=f[u][i];v=f[v][i];
	  }
	return(u);
  }
int main()
  {
  	scanf("%d%d",&n,&m);
  	for (i=1;i<n;i++) scanf("%d%d",&x,&y),add(x,y),add(y,x); 
  	
	  f[1][0]=1;d[1]=1;
	  dfs(1,-1);
  	for (i=1;i<=20;i++)
  	  for (j=1;j<=n;j++) 
  	    f[j][i]=f[f[j][i-1]][i-1];
  	for (i=1;i<=m;i++)
  	  {
  	  	scanf("%d%d%d",&x,&y,&z);
  	  	fa=lca(x,y);fb=lca(y,z);fc=lca(x,z);
  	  	if (fa==fb&&fb==fc)
  	  	  {
  	  	  	q=d[x]+d[y]+d[z]-3*d[fa];
  	  	  	printf("%d %d\n",fa,q);
  	  	  	continue;
		  }
		if (fa==fc) {swap(x,y);}
		if (fb==fc) {swap(y,z);}
		fa=lca(x,y);fb=lca(y,z);fc=lca(x,z);
		if (fa==fb)
		  {
		  	if (fc==z||fc==x)
		  	  {
		  	  	if (fc==z){q=d[x]+d[y]-2*d[fa];printf("%d %d\n",z,q);continue;}
				if (fc==x){q=d[z]+d[y]-2*d[fa];printf("%d %d\n",x,q);continue;}
				continue;
			  }
			else
			{
			 q=d[x]+d[z]-2*d[fc]+d[fc]+d[y]-2*d[lca(fc,y)];
			 printf("%d %d\n",fc,q);
		    }
		  }
	  }
  }


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值