【LCA】仓鼠找sugar

洛谷P3398

D e s c r i p t i o n Description Description

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

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

I n p u t Input Input

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

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

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

O u t p u t Output Output

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

S a m p l e Sample Sample I n p u t Input Input
5 5
2 5
4 2
1 3
1 4
5 1 5 1
2 2 1 4
4 1 3 4
3 1 1 5
3 5 1 4
S a m p l e Sample Sample O u t p u t Output Output
 Y
N
Y
Y
Y

H i n t Hint Hint

本题时限1s,内存限制128M,因新评测机速度较为接近NOIP评测机速度,请注意常数问题带来的影响。

20%的数据 n<=200,q<=200

40%的数据 n<=2000,q<=2000

70%的数据 n<=50000,q<=50000

100%的数据 n<=100000,q<=100000

T r a i n Train Train o f of of T h o u g h t Thought Thought

就一道LCA吧
就是一对的LCA在另一对数的最短路上
那如何判断一个点是否在某两个点的最短路上呢
假设 x = L C A ( a , b ) ,   y = L C A ( c , d ) x=LCA(a, b),\ y=LCA(c, d) x=LCA(a,b), y=LCA(c,d)
d e p [ i ] dep[i] dep[i]代表 i i i点到达根节点的距离,就是 i i i点的深度
d i s ( i , j ) dis(i, j) dis(i,j) i i i j j j的最短路,具体就是 d e p [ i ] + d e p [ j ] − d e p [ L C A ( i , j ) ] ∗ 2 dep[i] + dep[j] - dep[LCA(i, j)] * 2 dep[i]+dep[j]dep[LCA(i,j)]2(不懂的可以画图好吧)
那么若dis(x, c) + dis(x, d) = dis(c, d)
L C A ( a , b ) LCA(a, b) LCA(a,b)一定在 c c c d d d的最短路上
最后就可以直接判断了

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;

int dep[500005], Fa[500005][20], H[500005];
int n, m, t, tot, x, y, l, r;

struct whw
{
	int w, h;
}wh[1000005];

void DFS(int now, int k)
{
	Fa[now][0] = k;dep[now] = dep[k] + 1;
	
	for(int i = 1; i <= log2(dep[now]); ++i)
		Fa[now][i] = Fa[Fa[now][i - 1]][i - 1];
		
	for(int i = H[now]; i; i = wh[i].h)
		if(wh[i].w  != k)DFS(wh[i].w, now);
}

int LCA(int x, int y)
{
	if(dep[x] < dep[y])swap(x, y);
	
	for (int i = log2(n) + 1; i >= 0; --i)//不知道为什么这个对了而后面那个错了
		if (dep[Fa[x][i]] >= dep[y])
			x = Fa[x][i];
//	while(dep[x] > dep[y])x = Fa[x][(int)log2(dep[x] -dep[y]) - 1];我也不知道错在了哪
	if(x == y)return x;
	for(int i = log2(dep[x]) + 1; i >= 0; --i)
		if(Fa[x][i] != Fa[y][i])
			x = Fa[x][i], y = Fa[y][i];
	return Fa[x][0]; 
}

int Len(int a, int b)
{return dep[a] + dep[b] - dep[LCA(a, b)] * 2;}

void hw(int x, int y)
{wh[++tot] = (whw){y, H[x]}; H[x] = tot;}

int main()
{
	scanf("%d%d", &n, &m);
	for(int i = 1; i < n; ++i)
	{
		scanf("%d%d", &x, &y);
		hw(x, y); hw(y, x);
	}
	DFS(1, 0);
	
	for(int i = 1; i <= m; ++i)
	{
		scanf("%d%d%d%d", &x, &y, &l, &r);
		int a = LCA(x, y), b = LCA(l, r);
		if(Len(a, l) + Len(a, r) == Len(l, r)
		|| Len(b, x) + Len(b, y) == Len(x, y))printf("Y\n");
		else printf("N\n");
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值