B. Borrow Classroom
每年的BNU校赛都会有两次赛前培训,为此就需要去借教室,由于SK同学忙于出题,这个事情就由小Q同学来跑腿。SK同学准备从宿舍出发,把借教室的单子交给小Q同学让他拿去教务处盖章,但是何老师突然发现SK同学好像借错教室了,想抢在借教室的单子被送到教务处之前拦截下来。
现在把校园抽象成一棵个节点的树,每条边的长度都是一个单位长度,从到编号,其中教务处位于号节点,接下来有个询问,每次询问中SK同学会从号节点出发,到号节点找到小Q同学并将借教室的单子交给他,然后小Q同学再从号节点出发前往教务处,何老师会从号节点出发开始拦截。
所有人在一个单位时间内最多走一个单位距离,只要何老师在单子还没被送到教务处之前遇到拿着单子的同学都算拦截成功,如果小Q同学已经到了教务处,那么由于小Q同学手速极快,单子会被立即交上去,即使何老师到了教务处也无济于事,你需要判断何老师是否能够拦截成功。
Input
第一行是一个正整数,表示测试数据的组数,
对于每组测试数据,
第一行是两个整数,分别表示节点数和询问数,
接下来行,每行包含两个整数,表示和之间有一条边相连,保证这些边能构成一棵树,
接下来行,每行包含三个整数,表示一个询问,其中是何老师所在位置,是SK同学所在位置,是小Q同学所在位置,保证小Q同学初始不在教务处。
Output
对于每个询问,输出一行,如果何老师能成功拦截则输出"YES"(不含引号),否则输出"NO"(不含引号)。
Sample Input
1 7 2 1 2 2 3 3 4 4 7 1 5 1 6 3 5 6 7 5 6
Sample Output
YES NO
思路:
一共有q个查询,那么我们离线LCA时间复杂度O(n+q)来查询两点间距离。
显然送单子的路径一定是B----->C----->1的,那么我们过程维护LCA的同时,能够O(1)查询BC两点间距离以及C1两点间距离。
这个距离我们设定为L;
显然老师的路径是A-------------->1的,那么我们过程维护LCA的同时,也能够O(1)查询A1两点间距离。
这个距离我们设定为R;
显然若有:L>R.那么一定是YES。
若有L==R,我们要讨论,如果我们从点C到点1的路径和从点A到点1的路径上有一个点相交且不是1号节点,那么L==R的情况,也是YES的情况,那么这里我们只要查询一下A点和C点的LCA是不是1即可,如果是,那么结果就是YES,否则就是NO。
显然若有L<R,那么结果一定是NO。
Ac代码:
#include<stdio.h>
#include<string.h>
#include<vector>
using namespace std;
struct EdgeNode
{
int from;
int to;
int w;
int lca;
int next;
} e[1200000],q[1200000];
int dist[100040];
int A[100040];
int B[100040];
int C[100040];
int head[100040];
int f[100040];
int qhead[100040];
int vis[100040];
int dir[100040];
int problem[100040][2];
int n,m,ask,cont;
int find(int a)
{
int r=a;
while(f[r]!=r)
r=f[r];
int i=a;
int j;
while(i!=r)
{
j=f[i];
f[i]=r;
i=j;
}
return r;
}
void merge(int a,int b)
{
int A,B;
A=find(a);
B=find(b);
if(A!=B)
f[B]=A;
}
void add(int from,int to,int w)
{
e[cont].from=from;
e[cont].to=to;
e[cont].w=w;
e[cont].next=head[from];
head[from]=cont;
cont++;
e[cont].from=to;
e[cont].to=from;
e[cont].w=w;
e[cont].next=head[to];
head[to]=cont;
cont++;
}
void addq(int from,int to)
{
q[cont].from=from;
q[cont].to=to;
q[cont].lca=-1;
q[cont].next=qhead[from];
qhead[from]=cont;
cont++;
q[cont].from=to;
q[cont].to=from;
q[cont].lca=-1;
q[cont].next=qhead[to];
qhead[to]=cont;
cont++;
}
void LCA(int u)
{
f[u]=u;
vis[u]=1;
for(int k=head[u]; k!=-1; k=e[k].next)
{
int to=e[k].to;
if(vis[to]==0)
{
int w=e[k].w;
dir[to]=dir[u]+w;
LCA(to);
merge(u,to);
}
}
for(int k=qhead[u]; k!=-1; k=q[k].next)
{
int to=q[k].to;
if(vis[to]==1)
{
q[k].lca=find(to);
q[k^1].lca=q[k].lca;
}
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n,m;
scanf("%d%d",&n,&m);
cont=0;
for(int i=1; i<=n; i++)f[i]=i;
memset(vis,0,sizeof(vis));
memset(head,-1,sizeof(head));
memset(qhead,-1,sizeof(qhead));
memset(dir,0,sizeof(dir));
for(int i=0;i<n-1;i++)
{
int x,y;
scanf("%d%d",&x,&y);
add(x,y,1);
}
cont=0;
for(int i=0;i<m;i++)
{
scanf("%d%d%d",&A[i],&B[i],&C[i]);
addq(B[i],C[i]);
}
for(int i=0;i<m;i++)addq(A[i],C[i]);
LCA(1);
for(int i=0;i<m;i++)
{
int tmp=i*2;
int u=q[tmp].from,v=q[tmp].to,lca=q[tmp].lca;
int L=dir[u]+dir[v]-2*dir[lca]+dir[v];
int R=dir[A[i]];
if(R<L)printf("YES\n");
else if(R==L)
{
int tmp=(i+m)*2;
int lcaAC=q[tmp].lca;
if(lcaAC==1)printf("NO\n");
else printf("YES\n");
}
else printf("NO\n");
}
}
}