首页 > 技术文章 > 洛谷 P3398 仓鼠找sugar 题解

acioi 2019-09-15 20:43 原文

先说一下题意:

在一颗树上一个仓鼠从a走到b,一个他的基友从c走到d求他两个是不是会有相遇的地方。

一道求lca的题目。

这道题只看题目是很难找出规律的,但是一画图他的规律就很显然了

可以发现如果两条路径如果相交那一对点的最近公共祖先一定在另一台的路径上

不然如果相交那就这能两条边交叉,这在树中是不可能出现的
否则就不是一棵树

但是怎么确定这到底是不是回相遇的呢?

可以先求出仓鼠目的地b和出发点a的最近公共祖先qwq和surger的

目的地d和出发点c的最近公共祖先awa

然后分别求出a和d,a和c,b和d,b和c的最近公共祖先来和

前面求出的两个最近公共祖先的深度比较如果有一个这两个

深度小那就是不可行的不然如果都比他深度深或者等于的话

那就是可能遇见的

但是怎么证明只要他们的最近公共祖先的深度大于等于awa和qwq

那就是可行的呢?

这个在三克油大佬的博客里面已经证明的很好啦,我就不多说了
附上链接

这里

#include<iostream>
#include<cstdio>
#include<algorithm>

using namespace std;
const int Max = 100005;
struct node
{
	int y,ne;
}a[Max << 1];
int head[Max];
int f[Max][22];
int tot = 0;
void insert(int x,int y)
{
	a[++ tot].y = y;
	a[tot].ne = head[x];
	head[x] = tot;
}
bool use[Max];
int de[Max];
void dfs(int x)
{
	use[x] = true;
	for(int i = head[x];i != 0;i = a[i].ne)
	{
		int ii = a[i].y;
		if(use[ii] == false)
		{
			f[ii][0] = x;
			de[ii] = de[x] + 1;
			dfs(ii);
		}
	}
}

int lca(int x,int y)
{
	if(de[x] < de[y])swap(x,y);
	for(int j = 20;j >= 0;j --)
		if(de[f[x][j]] >= de[y])
			x = f[x][j];
	if(x == y)
		return x;
	for(int i = 20;i >= 0;i --)
		if(f[x][i] != f[y][i])
			x = f[x][i],y = f[y][i];
	return f[x][0];
}

int main()
{
	int n,q;
	int x,y;
	scanf("%d%d",&n,&q);
	for(int i = 1;i < n;++ i)
	{
		scanf("%d%d",&x,&y);
		insert(x,y);
		insert(y,x);
	}
	de[1] = 1;
	dfs(1);
	for(int j = 1;j <= 20;j ++)
		for(int i = 1;i <= n;++ i)
			f[i][j] = f[f[i][j - 1]][j - 1];
	while(q --)
	{
		int aa,bb,cc,dd;
		scanf("%d%d%d%d",&aa,&bb,&cc,&dd);
		int ab = lca(aa,bb);
		int cd = lca(cc,dd);
		
		//int ac = lca(aa,cc);
		//int ad = lca(aa,dd);
		//int bc = lca(bb,cc);
		//int bd = lca(bb,dd);
		int ans = max(de[lca(aa,cc)],de[lca(aa,dd)]);
		ans = max(ans,de[lca(bb,cc)]);
		ans = max(ans,de[lca(bb,dd)]);
		if(ans >= de[ab] && ans >= de[cd])
			cout<<"Y"<<endl;
		else
			cout<<"N"<<endl;
	}
	return 0;
}

推荐阅读