牛客算法周周练1 C. Borrow Classroom(二分/LCA)
题目链接:https://ac.nowcoder.com/acm/contest/5086/C
题意
一棵树上一个人要从结点 $B$ 移动到结点 $C$ 再移动到根节点 $1$,问另一个人能否从结点 $A$ 出发在根节点前拦截到第一个人。
题解
拦截成功的情况分为三种:
- $dis_{AC} ≤ dis_{BC}$
- $dis_{A1} < dis_{BC} + dis_{C1}$
- $dis_{A1} = dis_{BC} + dis_{C1},LCA_{AC}\ != 1$
代码
#include <bits/stdc++.h> using namespace std; const int N = 1e5 + 10; vector<int> e[N]; int parent[30][N]; int depth[N]; void dfs(int v, int u, int dep) { parent[0][v] = u; depth[v] = dep; for (auto w : e[v]) if (w != u) dfs(w, v, dep + 1); } int LCA(int u, int v) { if (depth[u] > depth[v]) swap(u, v); for (int k = 0; k < 30; k++) { if ((depth[v] - depth[u]) >> k & 1) { v = parent[k][v]; } } if (u == v) return u; for (int k = 30 - 1; k >= 0; k--) { if (parent[k][u] != parent[k][v]) { u = parent[k][u]; v = parent[k][v]; } } return parent[0][u]; } int dis(int u, int v) { int t = LCA(u, v); return depth[u] - depth[t] + depth[v] - depth[t]; } void solve() { for (auto &vec : e) vec.clear(); int n, q; cin >> n >> q; for (int i = 0; i < n - 1; i++) { int u, v; cin >> u >> v; e[u].push_back(v); e[v].push_back(u); } dfs(1, 0, 0); for (int k = 0; k + 1 < 30; k++) { for (int v = 1; v <= n; v++) { if (parent[k][v] == 0) parent[k + 1][v] = 0; else parent[k + 1][v] = parent[k][parent[k][v]]; } } for (int i = 0; i < q; i++) { int A, B, C; cin >> A >> B >> C; bool ok = false; int disAC = dis(A, C); int disBC = dis(B, C); int disA1 = depth[A] - depth[1]; int disC1 = depth[C] - depth[1]; if ((disAC <= disBC) or (disA1 < disBC + disC1) or (disA1 == disBC + disC1 and LCA(A, C) != 1)) ok = true; cout << (ok ? "YES" : "NO") << "\n"; } } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr); int t; cin >> t; while (t--) solve(); }

浙公网安备 33010602011771号