列表

详情


NC13813. Borrow Classroom

描述

每年的BNU校赛都会有两次赛前培训,为此就需要去借教室,由于SK同学忙于出题,这个事情就由小Q同学来跑腿。SK同学准备从宿舍出发,把借教室的单子交给小Q同学让他拿去教务处盖章,但是何老师突然发现SK同学好像借错教室了,想抢在借教室的单子被送到教务处之前拦截下来。

现在把校园抽象成一棵n个节点的树,每条边的长度都是一个单位长度,从1到n编号,其中教务处位于1号节点,接下来有q个询问,每次询问中SK同学会从B号节点出发,到C号节点找到小Q同学并将借教室的单子交给他,然后小Q同学再从C号节点出发前往教务处,何老师会从A号节点出发开始拦截。

所有人在一个单位时间内最多走一个单位距离,只要何老师在单子还没被送到教务处之前遇到拿着单子的同学都算拦截成功,如果小Q同学已经到了教务处,那么由于小Q同学手速极快,单子会被立即交上去,即使何老师到了教务处也无济于事,你需要判断何老师是否能够拦截成功。

输入描述

第一行是一个正整数T(≤ 5),表示测试数据的组数, 对于每组测试数据, 第一行是两个整数n,q(1≤ n,q ≤ 100000),分别表示节点数和询问数, 接下来n-1行,每行包含两个整数x,y(1≤ x,y ≤ n),表示x和y之间有一条边相连,保证这些边能构成一棵树, 接下来q行,每行包含三个整数A,B,C(1 ≤ A,B,C ≤ n),表示一个询问,其中A是何老师所在位置,B是SK同学所在位置,C是小Q同学所在位置,保证小Q同学初始不在教务处。

输出描述

对于每个询问,输出一行,如果何老师能成功拦截则输出"YES"(不含引号),否则输出"NO"(不含引号)。

示例1

输入:

1
7 2
1 2
2 3
3 4
4 7
1 5
1 6
3 5 6
7 5 6

输出:

YES
NO

原站题解

上次编辑到这里,代码来自缓存 点击恢复默认模板

C++(clang++ 11.0.1) 解法, 执行用时: 1535ms, 内存消耗: 23488K, 提交时间: 2022-08-26 21:17:45

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
vector<int>e[N];
int dep[N],fa[N][20],mx,n,q,t;
void dfs(int u){
	for(auto v:e[u])
		if(v!=fa[u][0]){
			fa[v][0]=u,dep[v]=dep[u]+1;
			for(int i=1;i<=mx;i++)
				fa[v][i]=fa[fa[v][i-1]][i-1];
			dfs(v);
		}
}
int lca(int u,int v){
	if(dep[u]<dep[v]) swap(u,v);
	int dta=dep[u]-dep[v];
	for(int i=0;i<=mx;i++)
		if((1<<i)&dta) u=fa[u][i];
	if(u==v) return u;
	for(int i=mx;i>=0;i--)
		if(fa[u][i]!=fa[v][i])
			u=fa[u][i],v=fa[v][i];
	return fa[u][0];
}
int main(){
	cin>>t;
	while(t--){
		for(int i=1;i<=n;i++) e[i].clear();
		cin>>n>>q;
		mx=log2(n),dep[1]=0;
		for(int i=1,u,v;i<n;i++){
			cin>>u>>v;
			e[u].push_back(v);
			e[v].push_back(u); 
		}
		dfs(1);
		while(q--){
			int a,b,c,d1,d2;
			cin>>a>>b>>c;
			d1=dep[a],d2=dep[b]+dep[c]*2-2*dep[lca(b,c)];
			if(d1<d2) puts("YES");
			else if(d1>d2) puts("NO");
			else puts(lca(a,c)==1?"NO":"YES");
		}
	}
	return 0;
}

上一题