洛谷3398 仓鼠找sugar

题目描述

小仓鼠的和他的基(mei)友(zi)sugar住在地下洞穴中,每个节点的编号为1~n。地下洞穴是一个树形结构。这一天小仓鼠打算从从他的卧室(a)到餐厅(b),而他的基友同时要从他的卧室(c)到图书馆(d)。他们都会走最短路径。现在小仓鼠希望知道,有没有可能在某个地方,可以碰到他的基友?

小仓鼠那么弱,还要天天被zzq大爷虐,请你快来救救他吧!

输入格式

第一行两个正整数n和q,表示这棵树节点的个数和询问的个数。

接下来n-1行,每行两个正整数u和v,表示节点u到节点v之间有一条边。

接下来q行,每行四个正整数a、b、c和d,表示节点编号,也就是一次询问,其意义如上。

输出格式

对于每个询问,如果有公共点,输出大写字母“Y”;否则输出“N”。


思路

这道题与洛谷6374树上询问中判断c是否在a,b到lca(a,b)的路径上的思路如出一辙。若两条路径有交点,显然lca(a,b)或lca(c,d)必然是交点之一。将lca(a,b)或lca(c,d)当作树上询问中的c,分别判断是否在另外两个点到它们lca的路径上即可。


代码

#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
const int N=200000,P=18;
int n,q;
int dep[N],st[N][P+1];
vector <int> ed[N];
void dfs(int x,int fa)
{
	dep[x]=dep[fa]+1;
	st[x][0]=fa;
	int len=ed[x].size();
	for(int i=0;i<len;i++)
	if(ed[x][i]!=st[x][0]) dfs(ed[x][i],x);
}
int lca(int x,int y)
{
	if(dep[x]>dep[y]) swap(x,y);
	int dif=dep[y]-dep[x],step=0;
	while(dif)
	{
		if(dif&1) y=st[y][step];
		dif>>=1,step++;
	}
	if(x==y) return x;
	step=P+1;
	while(--step>=0)
	if(st[x][step]!=st[y][step])
	x=st[x][step],y=st[y][step];
	return st[x][0];
}
int main()
{
	scanf("%d%d",&n,&q);
	int u,v;
	for(int i=1;i<n;i++)
	{
		scanf("%d%d",&u,&v);
		ed[u].push_back(v),ed[v].push_back(u); 
	}
	dfs(1,0);
	for(int i=1;i<=P;i++)
	for(int j=1;j<=n;j++)
	st[j][i]=st[st[j][i-1]][i-1];
	int a,b,c,d;
	char ans;
	for(int i=1;i<=q;i++)
	{
		ans='N';//若下面的条件都不满足,则显然不能碰见 
		scanf("%d%d%d%d",&a,&b,&c,&d);
		int anc1=lca(a,b),anc2=lca(c,d),anc3=lca(anc1,anc2);
		if(anc3==anc1&&(lca(anc2,a)==anc2||lca(anc2,b)==anc2)) ans='Y';
		//若c,d的lca在以a,b的lca为根的子树上,这时再判断c,d的lca是否在a或b到a,b的lca的路径上 
		if(anc3==anc2&&(lca(anc1,c)==anc1||lca(anc1,d)==anc1)) ans='Y';
		//若a,b的lca在以c,d的lca为根的子树上,这时再判断a,b的lca是否在c或d到c,d的lca的路径上 
		printf("%c\n",ans); 
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值