仓鼠找sugar

4 篇文章 0 订阅

P3398 仓鼠找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”。

输入输出样例
输入 #1复制
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
输出 #1复制
Y
N
Y
Y
Y
说明/提示
本题时限1s,内存限制128M,因新评测机速度较为接近NOIP评测机速度,请注意常数问题带来的影响。

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

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

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

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

思路:

对于树上的两段路而言,若有重合,则这两段路的四个焦点的各自的lca的lca必定为这两个中的其中一个。

即:

在这里插入图片描述
节点“1” && “2”的lca必定为节点“1” || “2”

但也会有一个bug…………

如图:

在这里插入图片描述
就会发现挂了…………qwq

继续推…………

然后又会有一个发现,若lca ( d,c )在a ~ lca ( a,b ) || b ~ lca ( a,b )上

则说明可行…………但OJBK了么…………qwq…………并没有…………

还可能是lca ( a,b)在c ~ lca ( c,d ) || d ~ lca ( c,d )上

则要反过来………………

#include <algorithm>
#include <iostream>
#include <cstring>
#include <vector>
#include <cstdio>
#include <queue>
#include <cmath>
#define LL long long
using namespace std;

const int N=5*1e5+100,M=1001;
int n,m,u,v,a,b,c,d,f[N][30],dis[N],cnt[N],high[N];
vector < int > q[N];
void dfs ( int rt,int fa,int h )
{
	f[rt][0]=fa;
	high[rt]=h++;
	int len=q[rt].size ( );
	for ( int i=0;i<len;i++ )
	{
		int to=q[rt][i];
		if ( to!=fa ) dfs ( to,rt,h );
	}
}
int lca ( int x,int y )
{
	if ( high[x]>high[y] ) swap ( x,y );
	int c=high[y]-high[x],k=21,m=1<<k;
	while ( c )
	{
		if ( c>=m ) y=f[y][k],c-=m;
		m/=2,k--;
	}
	if ( x==y ) return x;
	k=19;
	while ( k>=0 ) { if ( f[x][k]!=f[y][k] ) x=f[x][k],y=f[y][k];k--; }
	return f[x][0];
}

int main ( )
{
	scanf ( "%d %d",&n,&m );
	for ( int i=1;i<n;i++ )
	{
		scanf ( "%d %d",&u,&v );
		q[u].push_back ( v ),q[v].push_back (u);
	}
	dfs ( 1,0,1 );
	for ( int j=1;j<=21;j++ ) for ( int i=1;i<=n;i++ ) f[i][j]=f[f[i][j-1]][j-1];
	while ( m-- )
	{
		scanf ( "%d %d %d %d",&a,&b,&c,&d );
		int ab=lca ( a,b ),cd=lca ( c,d ),ab_cd=lca ( ab,cd );
		if ( ab_cd==ab || ab_cd==cd )   //大前提
		{
			if ( high[ab]>high[cd] ) swap ( ab,cd ),swap ( a,c ),swap ( b,d );   //交换
			int cd_a=lca ( cd,a ),cd_b=lca ( cd,b );
			if ( cd_a==cd || cd_b==cd ) printf ( "Y\n" );      //在路径上
			else printf ( "N\n" );
		}
		else printf ( "N\n" );
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值