链接:https://ac.nowcoder.com/acm/problem/13813
来源:牛客网
题目描述
每年的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
思路:通过分析可知,若A想拦截到B,则A一定会选择一个B的必经点拦截到B,基于树的特殊结构,我们发现树根是一个最优选择,当然有人可能会想问,为什么不选择A和B的最近公共祖先呢?
我们要知道的是B不是直接去教务处,她需先去找C,而如果直接堵最近公共祖先就会发现,很多时候你本来能拦截到B,但是最后没有拦截到,例子我就不举了,而树根一定是一个最优选择,并且A能成功拦截的条件一定是A比C先到达教务处的,我们只需要预处理出A,B和C到树根的距离即可,而求解实际行走距离的时候需要借助最近公共祖先(LCA)。
#include<math.h>
#include<vector>
#include<stdio.h>
#include<algorithm>
using namespace std;
#define maxn 100005
int f[maxn][20],dep[maxn],mx,n,q,t;
vector<int>e[maxn];
void dfs(int u){
int sz=e[u].size();
for(int i=0;i<sz;i++){
int v=e[u][i];
if(v==f[u][0])
continue;
f[v][0]=u;
dep[v]=dep[u]+1;
for(int i=1;i<=mx;i++)
f[v][i]=f[f[v][i-1]][i-1];
dfs(v);
}
}
int lca(int x,int y){
if(dep[x]<dep[y])
swap(x, y);
for(int i=mx;i>=0;i--)
if(dep[x]-(1<<i)>=dep[y])
x=f[x][i];
if(x==y)
return x;
for(int i=mx;i>=0;i--)
if(f[x][i]!=f[y][i])
x=f[x][i],y=f[y][i];
return f[x][0];
}
int main(void){
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&q);
for(int i=0,x,y;i<n-1;i++){
scanf("%d%d",&x,&y);
e[x].push_back(y);
e[y].push_back(x);
}
dep[1]=0;
mx=log2(n);
dfs(1);
while(q--){
int a,b,c,d1,d2;
scanf("%d%d%d",&a,&b,&c);
d1=dep[a];
d2=dep[b]+dep[c]*2-2*dep[lca(b,c)];
if(d1>d2) printf("NO\n");
else if(d1<d2) printf("YES\n");
else lca(a,c)==1?printf("NO\n"):printf("YES\n");
}
for(int i=1;i<=n;i++)
e[i].clear();
}
return 0;
}