题面:
每年的BNU校赛都会有两次赛前培训,为此就需要去借教室,由于SK同学忙于出题,这个事情就由小Q同学来跑腿。SK同学准备从宿舍出发,把借教室的单子交给小Q同学让他拿去教务处盖章,但是何老师突然发现SK同学好像借错教室了,想抢在借教室的单子被送到教务处之前拦截下来。
现在把校园抽象成一棵n个节点的树,每条边的长度都是一个单位长度,从1到n编号,其中教务处位于1号节点,接下来有q个询问,每次询问中SK同学会从B号节点出发,到C号节点找到小Q同学并将借教室的单子交给他,然后小Q同学再从C号节点出发前往教务处,何老师会从A号节点出发开始拦截。
所有人在一个单位时间内最多走一个单位距离,只要何老师在单子还没被送到教务处之前遇到拿着单子的同学都算拦截成功,如果小Q同学已经到了教务处,那么由于小Q同学手速极快,单子会被立即交上去,即使何老师到了教务处也无济于事,你需要判断何老师是否能够拦截成功。
输入:
1
7 2
1 2
2 3
3 4
4 7
1 5
1 6
3 5 6
7 5 6
输出:
YES
NO
思路:
~~ 乍一看,B先到C,C到1,A要在1之前拦截住,马上就很懵逼。但 就只询问是否拦截住。就发现 是个水题。~~
认真来讲:
一看是树,然后是树上的距离问题,题面给的意思是 A既能拦截B,也能拦截C,但是无需考虑那么多,只需考虑A是否能拦截住C,也就是
距离(A->1) 与 距离(B->C + C->1) 进行比较。
坑点:
很好容易理解吧。有个小小的坑点,上述两个距离相等咋办,思考下,其实A和C会相遇,就容易想到,只要A和C 相遇点不是1就可以。
so 就是lca模板题。
贴上代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e6+177;
struct Node{
int next;
int to;
int vul;
}edge[maxn];
int head[maxn];
int tot;
int f[maxn][25],d[maxn],dist[maxn];
int t;
queue<int >q;
void add(int from,int to,int vul){
edge[++tot].next=head[from];
edge[tot].to=to;
edge[tot].vul=vul;
head[from]=tot;
}
void init(){
tot=0;
memset(head,0,sizeof(head));
memset(d,0,sizeof(d));
}
void bfs(){
q.push(1);
d[1]=1;
while(q.size()){
int x=q.front();
q.pop();
for(int i=head[x];i;i=edge[i].next){
int y=edge[i].to;
if(d[y])continue;
d[y]=d[x]+1;
dist[y]=dist[x]+edge[i].vul;
f[y][0]=x;
for(int j=1;j<=t;j++){
f[y][j]=f[f[y][j-1]][j-1];
}
q.push(y);
}
}
}
int lca(int x,int y){
if(d[x]>d[y]){
swap(x,y);
}
for(int i=t;i>=0;i--){
if(d[f[y][i]]>=d[x]){
y=f[y][i];
}
}
if(x==y){
return x;
}
for(int i=t;i>=0;i--){
if(f[x][i]!=f[y][i]){
x=f[x][i];
y=f[y][i];
}
}
return f[x][0];
}
int main(){
int T;
cin>>T;
while(T--){
init();
int n,q;
cin>>n>>q;
t=(int)(log(n)/log(2))+1;
int x,y;
for(int i=0;i<n-1;i++){
cin>>x>>y;
add(x,y,1);
add(y,x,1);
}
bfs();
int a,b,c;
for(int i=0;i<q;i++){
cin>>a>>b>>c;
if(dist[a]<dist[b]+2*dist[c]-2*dist[lca(b,c)]){
cout<<"YES"<<endl;
}else if(lca(a,c)!=1&&dist[a]==dist[b]+2*dist[c]-2*dist[lca(b,c)]){
cout<<"YES"<<endl;
}else{
cout<<"NO"<<endl;
}
}
}
}