第十五届北京师范大学程序设计竞赛决赛
B. Borrow Classroom
每年的BNU校赛都会有两次赛前培训,为此就需要去借教室,由于SK同学忙于出题,这个事情就由小Q同学来跑腿。SK同学准备从宿舍出发,把借教室的单子交给小Q同学让他拿去教务处盖章,但是何老师突然发现SK同学好像借错教室了,想抢在借教室的单子被送到教务处之前拦截下来。
现在把校园抽象成一棵n个节点的树,每条边的长度都是一个单位长度,从1到n编号,其中教务处位于1号节点,接下来有q个询问,每次询问中SK同学会从B号节点出发,到C号节点找到小Q同学并将借教室的单子交给他,然后小Q同学再从C号节点出发前往教务处,何老师会从A号节点出发开始拦截。
所有人在一个单位时间内最多走一个单位距离,只要何老师在单子还没被送到教务处之前遇到拿着单子的同学都算拦截成功,如果小Q同学已经到了教务处,那么由于小Q同学手速极快,单子会被立即交上去,即使何老师到了教务处也无济于事,你需要判断何老师是否能够拦截成功。
Input
第一行是一个正整数 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同学初始不在教务处。
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
题解
首先要支持求树上两点的
LCA
和一条到跟的路径上的到是第二个点的位置。
判断是否可行,我们只需要判断当在
b→c→1
路径上走,走到倒数第二个点(1的孩子)的时候
a
点走到了哪里,如果
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 200010;
int T, n, m;
int head[maxn], nxt[maxn<<1], to[maxn<<1], cnt;
int dep[maxn], top[maxn], sz[maxn], son[maxn], st[maxn], tp;
int q[maxn], l, r, fa[maxn];
bool vis[maxn];
void add(int a, int b){nxt[++ cnt] = head[a], to[head[a] = cnt] = b;}
void pre(){
l = 1, r = 0, tp = 0;
q[++ r] = 1, vis[1] = 1, dep[1] = 0;
while(l <= r){
int x = q[l ++]; sz[x] = 1;
for(int i = head[x]; i; i = nxt[i]){
int u = to[i]; if(vis[u] || fa[x] == u) continue;
vis[u] = 1, dep[u] = dep[x] + 1, fa[u] = x;
q[++ r] = u;
}
}
for(int i = n; i >= 1; i --){
int x = q[i]; if(fa[x] == 0) continue;
sz[fa[x]] += sz[x];
if(sz[son[fa[x]]] < sz[x]) son[fa[x]] = x;
}
st[++ tp] = 1, top[1] = 1;
while(tp){
int x = st[tp --];
for(int i = head[x]; i; i = nxt[i]){
int u = to[i]; if(u == son[x] || u == fa[x]) continue;
st[++ tp] = u, top[u] = u;
}
if(son[x]) st[++ tp] = son[x], top[son[x]] = top[x];
}
}
int lca(int x, int y){
while(top[x] != top[y]){
if(dep[top[x]] < dep[top[y]]) swap(x, y);
x = fa[top[x]];
}
return dep[x] < dep[y] ? x : y;
}
int find(int x){
if(x == 1)
return -1;
while(true){
if(top[x] == 1) return son[1];
if(dep[top[x]] == 1) return top[x];
x = fa[top[x]];
}
}
int main(){
scanf("%d", &T);
while(T --){
scanf("%d%d", &n, &m), cnt = 0;
for(int i = 1; i <= n; i ++) head[i] = 0, son[i] = 0, vis[i] = 0;
for(int i = 1, a, b; i < n; i ++){
scanf("%d%d", &a, &b);
add(a, b), add(b, a);
}
pre();
for(int i = 1, a, b, c; i <= m; i ++){
scanf("%d%d%d", &a, &b, &c);
if(b == 1 && c == 1 && (puts("NO"),true)) continue;
int w = dep[b] + 2 * dep[c] - 2 * dep[lca(b,c)];
if(dep[a] < w) puts("YES");
else if(dep[a] + (find(a) == (c == 1 ? find(b) : find(c)) ? -1 : 1) <= w - 1) puts("YES");
else puts("NO");
}
}
return 0;
}