bzoj-1787

Description

Input

Output

Sample Input

6 4
1 2
2 3
2 4
4 5
5 6
4 5 6
6 3 1
2 4 4
6 6 6

Sample Output


5 2
2 5
4 1
6 0

HINT

思路:很清晰,答案显然是三点中两点lca之间的一个

本题的题解也比较完善了,p点应该取的是3对点lca之间不同的那一个,这里详细证明一下

假设lca(x,y)=p1 lca(x,z)=p1 lca(y,z)=p2

那么显然,p2到y,z的距离小于p1到y,z的距离。

假设p1到p2之间的距离为l

如果点选了p1,那么总长度应该是len(x,p1)+(len(y,p2)+l)+(len(z,p2)+l)

如果点选了p2,那么总长度应该是(len(x,p1)+l)+len(y,p2)+len(z,p2);

因为树上的话两点路径唯一,所以x到p2只能通过p1同理y,z到达p1必须通过p2

显然得到p2为最优解

lca过程不再赘述,别的题解上应该都有详细分析。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
int poin[1000001],next[1000001],h[6000001];
int f[600001][30],dep[600001],cnt;
int n,m,s;
inline void add(int x,int y)
{
	poin[++cnt]=y;next[cnt]=h[x];h[x]=cnt;
}
inline void dfs(int poi,int fa,int depth)
{
	dep[poi]=depth;
	f[poi][0]=fa;
	for (int i=h[poi];i;i=next[i])
		if (poin[i]!=fa)	dfs(poin[i],poi,depth+1);	
}
inline int up(int x,int de)
{
	while (dep[x]!=de)
	{
		
		int tmp=0;
		while (dep[f[x][tmp]]>=de)	tmp++;
		x=f[x][tmp-1];
	}
	return x;
}
inline int get(int x,int y)
{
	if (dep[x]>dep[y])	x=up(x,dep[y]);else if (dep[y]>dep[x]) y=up(y,dep[x]);	
	if(x==y)	return x;
	while (1)
	{
		int tmp=0;
		if (f[x][tmp]==f[y][tmp])	return f[x][tmp];	
		while (f[x][tmp]!=f[y][tmp])	tmp++;
		tmp--;
		x=f[x][tmp];y=f[y][tmp];
	}
}
inline int cal(int x,int y)
{
	int t=get(x,y);
	return abs(dep[t]-dep[x])+abs(dep[t]-dep[y]);
}
inline void doit(int x,int y,int z)
{
	int p1=get(x,y),p2=get(y,z),p3=get(x,z),p;
	if (p1==p2)	p=p3;
	if (p1==p3)	p=p2;
	if (p2==p3)	p=p1;
	int ans=cal(p,x)+cal(p,y)+cal(p,z);
	printf("%d %d\n",p,ans);
}

int main()
{
	cin>>n>>m;
	for(int i=1;i<=n-1;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		add(x,y);
		add(y,x);
	}	

	dfs(1,-1,1);	
	for (int j=1;j<=log2(n);j++)
		for (int i=1;i<=n;i++)
		{	
			int tmp=f[f[i][j-1]][j-1];
			if (f[i][j-1])
			f[i][j]=tmp;		
		}
	for (int i=1;i<=m;i++)
	{
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		doit(x,y,z);
	}
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值