寻找中点【lca】

题目描述
Windy 和 Zero 居住在同一个国家,该国家有N个城市。Windy 居住在 X 城市,Zero 居住在 Y 城市。任意两个城市之间有且只有一条路径相通(中间可能经过其它城市)。有一天,Windy 和 Zero 想见面,他们想把见面的地点定在城市 X 和城市 Y 的中间。现在请你告诉他们会见地点应该在哪里?


输入格式
第一行是一个整数 N(1≤N≤10 000),表示有 N 个城市。
接下来有 N-1 行,每行有两个整数 A,B,表示有一条路直接连接城市 A 和城市 B。
(1≤A,B≤N)
接下来有一个整数 M(1≤M≤100 000),表示下面有 M 个询问。
接下来有 M 行,每行有两个整数 X,Y,表示 Windy 和 Zero 居住的城市编号。
(1≤X,Y≤N)


输出格式
对于每个询问,如果城市 X 和城市 Y 的中点在一个城市上,则输出该中点城市的编号;如果这个中点在某条路径上,则输出这条路径两个端点的城市编号(用一个空格隔开),并且首先输出离城市X近的城市的编号。


样例数据 1
输入  [复制] 



1 2 
2 3 
2 4 
4 5 
6 4 

5 3 
3 5 
6 5
输出


4 2 
2 4 
4



-------------------------------------------------------------------------------------------------------

思路很简单 可以看出给出的图是一棵树 要找两个点之间的中点 相当于找他们之间的距离 然后除以二 通过其中一个点往上爬这样的距离

但是有几点细节

1.在我们从一个点往上找离这个点距离d的点时候 可以会用到一个函数

    我们姑且命名其为get_pos

    在写get_pos的时候...


2.在询问两点的时候 我们设置一个函数query

    注意 如果距离%2==0 那么说明只有一个中点 不会出现两个

    这个时候 不能直接输出这两个点的lca(我卡了很久)

 然后看代码吧


#include<bits/stdc++.h>
using namespace std;
int n,m,tot;
struct node
{
	int next;
	int to;
}edge[2*10005];
int first[10005],deepth[10005],fa[10005][20];
inline void add(int x,int y)
{
	++tot;
	edge[tot].to=y;
	edge[tot].next=first[x];
	first[x]=tot;
}
void dfs(int now,int father)
{
	deepth[now]=deepth[father]+1;
	fa[now][0]=father;
	for(int i=1;(1<<i)<=deepth[now];i++)
	{
		fa[now][i]=fa[fa[now][i-1]][i-1]; 
	}
	for(int i=first[now];i;i=edge[i].next)
	{
		int vis=edge[i].to;
		if(vis!=father)	dfs(vis,now);
	}
}
int lca(int x,int y)
{
	if(deepth[x]<deepth[y])	swap(x,y);
	for(int i=17;i>=0;i--)
	{
		if(deepth[fa[x][i]]>=deepth[y])	x=fa[x][i];
	}
	if(x==y)	return x;
	for(int i=17;i>=0;i--)
	{
		if(fa[x][i]!=fa[y][i])
		{
			x=fa[x][i];
			y=fa[y][i];
		}
	}
	return fa[x][0];
}
int get_pos(int u,int d)
{
	//注意一定要新定义一个 不然你会gg 
	int a=u;
	//get u往上面d距离的点 
	for(int i=17;i>=0;i--)
	{
		if(deepth[u]-deepth[fa[a][i]]<=d)	a=fa[a][i];
	}
	return a;
}
void query(int x,int y)
{
	int ancestor=lca(x,y);
//	cout<<
	int d=deepth[x]+deepth[y]-2*deepth[ancestor];
	if(d%2==0)
	{
		cout<<get_pos((deepth[x]>deepth[y]?x:y),d/2)<<endl;
	}
	else
	{
		if(deepth[x]>deepth[y])
		{
			int pos=get_pos(x,d/2);
			cout<<pos<<" ";
			cout<<fa[pos][0]<<endl;
		}
		else
		{
			int pos=get_pos(y,d/2);
			cout<<fa[pos][0]<<" "<<pos<<endl;
		}
	}
}
int main()
{
	//freopen("wordin.in","r",stdin);
	scanf("%d",&n);
	for(int i=1;i<=n-1;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		add(x,y);
		add(y,x);
	}
	scanf("%d",&m);
	dfs(1,0);
	for(int i=1;i<=m;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		query(x,y);
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值