This documentation is automatically generated by online-judge-tools/verification-helper
View the Project on GitHub suisen-cp/cp-library-cpp
#define PROBLEM "https://judge.yosupo.jp/problem/lca" #include <iostream> #include "library/tree/heavy_light_decomposition.hpp" using suisen::HeavyLightDecomposition; int main() { std::ios::sync_with_stdio(false); std::cin.tie(nullptr); int n, q; std::cin >> n >> q; std::vector<std::vector<int>> g(n); for (int i = 1; i < n; ++i) { int p; std::cin >> p; g[p].push_back(i); g[i].push_back(p); } HeavyLightDecomposition hld(g); auto lca = [&](int u, int v) -> int { int du = hld.get_depth(u); int dv = hld.get_depth(v); if (du < dv) { std::swap(u, v); std::swap(du, dv); } int l = -1, r = dv; while (r - l > 1) { int k = (l + r) >> 1; (hld.la(u, k + (du - dv)) == hld.la(v, k) ? r : l) = k; } return hld.la(v, r); }; for (int query_id = 0; query_id < q; ++query_id) { int u, v; std::cin >> u >> v; std::cout << lca(u, v) << '\n'; } return 0; }
#line 1 "test/src/tree/heavy_light_decomposition/la_based_lowest_common_ancestor.test.cpp" #define PROBLEM "https://judge.yosupo.jp/problem/lca" #include <iostream> #line 1 "library/tree/heavy_light_decomposition.hpp" #line 1 "library/type_traits/type_traits.hpp" #include <limits> #line 6 "library/type_traits/type_traits.hpp" #include <type_traits> namespace suisen { template <typename ...Constraints> using constraints_t = std::enable_if_t<std::conjunction_v<Constraints...>, std::nullptr_t>; template <typename T, typename = std::nullptr_t> struct bitnum { static constexpr int value = 0; }; template <typename T> struct bitnum<T, constraints_t<std::is_integral<T>>> { static constexpr int value = std::numeric_limits<std::make_unsigned_t<T>>::digits; }; template <typename T> static constexpr int bitnum_v = bitnum<T>::value; template <typename T, size_t n> struct is_nbit { static constexpr bool value = bitnum_v<T> == n; }; template <typename T, size_t n> static constexpr bool is_nbit_v = is_nbit<T, n>::value; template <typename T, typename = std::nullptr_t> struct safely_multipliable { using type = T; }; template <typename T> struct safely_multipliable<T, constraints_t<std::is_signed<T>, is_nbit<T, 32>>> { using type = long long; }; template <typename T> struct safely_multipliable<T, constraints_t<std::is_signed<T>, is_nbit<T, 64>>> { using type = __int128_t; }; template <typename T> struct safely_multipliable<T, constraints_t<std::is_unsigned<T>, is_nbit<T, 32>>> { using type = unsigned long long; }; template <typename T> struct safely_multipliable<T, constraints_t<std::is_unsigned<T>, is_nbit<T, 64>>> { using type = __uint128_t; }; template <typename T> using safely_multipliable_t = typename safely_multipliable<T>::type; template <typename T, typename = void> struct rec_value_type { using type = T; }; template <typename T> struct rec_value_type<T, std::void_t<typename T::value_type>> { using type = typename rec_value_type<typename T::value_type>::type; }; template <typename T> using rec_value_type_t = typename rec_value_type<T>::type; template <typename T> class is_iterable { template <typename T_> static auto test(T_ e) -> decltype(e.begin(), e.end(), std::true_type{}); static std::false_type test(...); public: static constexpr bool value = decltype(test(std::declval<T>()))::value; }; template <typename T> static constexpr bool is_iterable_v = is_iterable<T>::value; template <typename T> class is_writable { template <typename T_> static auto test(T_ e) -> decltype(std::declval<std::ostream&>() << e, std::true_type{}); static std::false_type test(...); public: static constexpr bool value = decltype(test(std::declval<T>()))::value; }; template <typename T> static constexpr bool is_writable_v = is_writable<T>::value; template <typename T> class is_readable { template <typename T_> static auto test(T_ e) -> decltype(std::declval<std::istream&>() >> e, std::true_type{}); static std::false_type test(...); public: static constexpr bool value = decltype(test(std::declval<T>()))::value; }; template <typename T> static constexpr bool is_readable_v = is_readable<T>::value; } // namespace suisen #line 5 "library/tree/heavy_light_decomposition.hpp" #include <vector> namespace suisen { class HeavyLightDecomposition { public: template <typename Q> using is_point_update_query = std::is_invocable<Q, int>; template <typename Q> using is_range_update_query = std::is_invocable<Q, int, int>; template <typename Q, typename T> using is_point_get_query = std::is_same<std::invoke_result_t<Q, int>, T>; template <typename Q, typename T> using is_range_fold_query = std::is_same<std::invoke_result_t<Q, int, int>, T>; using Graph = std::vector<std::vector<int>>; HeavyLightDecomposition() = default; HeavyLightDecomposition(Graph &g) : n(g.size()), visit(n), leave(n), head(n), ord(n), siz(n), par(n, -1), dep(n, 0) { for (int i = 0; i < n; ++i) if (par[i] < 0) dfs(g, i, -1); int time = 0; for (int i = 0; i < n; ++i) if (par[i] < 0) hld(g, i, -1, time); } HeavyLightDecomposition(Graph &g, const std::vector<int> &roots) : n(g.size()), visit(n), leave(n), head(n), ord(n), siz(n), par(n, -1), dep(n, 0) { for (int i : roots) dfs(g, i, -1); int time = 0; for (int i : roots) hld(g, i, -1, time); } int size() const { return n; } int lca(int u, int v) const { for (;; v = par[head[v]]) { if (visit[u] > visit[v]) std::swap(u, v); if (head[u] == head[v]) return u; } } int la(int u, int k, int default_value = -1) const { if (k < 0) return default_value; while (u >= 0) { int h = head[u]; if (visit[u] - k >= visit[h]) return ord[visit[u] - k]; k -= visit[u] - visit[h] + 1; u = par[h]; } return default_value; } int jump(int u, int v, int d, int default_value = -1) const { if (d < 0) return default_value; const int w = lca(u, v); int uw = dep[u] - dep[w]; if (d <= uw) return la(u, d); int vw = dep[v] - dep[w]; return d <= uw + vw ? la(v, (uw + vw) - d) : default_value; } int dist(int u, int v) const { return dep[u] + dep[v] - 2 * dep[lca(u, v)]; } template <typename T, typename Q, typename F, constraints_t<is_range_fold_query<Q, T>, std::is_invocable_r<T, F, T, T>> = nullptr> T fold_path(int u, int v, T identity, F bin_op, Q fold_query, bool is_edge_query = false) const { T res = identity; for (;; v = par[head[v]]) { if (visit[u] > visit[v]) std::swap(u, v); if (head[u] == head[v]) break; res = bin_op(fold_query(visit[head[v]], visit[v] + 1), res); } return bin_op(fold_query(visit[u] + is_edge_query, visit[v] + 1), res); } template < typename T, typename Q1, typename Q2, typename F, constraints_t<is_range_fold_query<Q1, T>, is_range_fold_query<Q2, T>, std::is_invocable_r<T, F, T, T>> = nullptr > T fold_path_noncommutative(int u, int v, T identity, F bin_op, Q1 fold_query, Q2 fold_query_rev, bool is_edge_query = false) const { T res_u = identity, res_v = identity; // a := lca(u, v) // res = fold(u -> a) + fold(a -> v) while (head[u] != head[v]) { if (visit[u] < visit[v]) { // a -> v res_v = bin_op(fold_query(visit[head[v]], visit[v] + 1), res_v); v = par[head[v]]; } else { // u -> a res_u = bin_op(res_u, fold_query_rev(visit[head[u]], visit[u] + 1)); u = par[head[u]]; } } if (visit[u] < visit[v]) { // a = u res_v = bin_op(fold_query(visit[u] + is_edge_query, visit[v] + 1), res_v); } else { // a = v res_u = bin_op(res_u, fold_query_rev(visit[v] + is_edge_query, visit[u] + 1)); } return bin_op(res_u, res_v); } template <typename Q, constraints_t<is_range_update_query<Q>> = nullptr> void update_path(int u, int v, Q update_query, bool is_edge_query = false) const { for (;; v = par[head[v]]) { if (visit[u] > visit[v]) std::swap(u, v); if (head[u] == head[v]) break; update_query(visit[head[v]], visit[v] + 1); } update_query(visit[u] + is_edge_query, visit[v] + 1); } template <typename T, typename Q, constraints_t<is_range_fold_query<Q, T>> = nullptr> T fold_subtree(int u, Q fold_query, bool is_edge_query = false) const { return fold_query(visit[u] + is_edge_query, leave[u]); } template <typename Q, constraints_t<is_range_update_query<Q>> = nullptr> void update_subtree(int u, Q update_query, bool is_edge_query = false) const { update_query(visit[u] + is_edge_query, leave[u]); } template <typename T, typename Q, constraints_t<is_point_get_query<Q, T>> = nullptr> T get_point(int u, Q get_query) const { return get_query(visit[u]); } template <typename Q, constraints_t<is_point_update_query<Q>> = nullptr> void update_point(int u, Q update_query) const { update_query(visit[u]); } std::vector<int> inv_ids() const { std::vector<int> inv(n); for (int i = 0; i < n; ++i) inv[visit[i]] = i; return inv; } int get_visit_time(int u) const { return visit[u]; } int get_leave_time(int u) const { return leave[u]; } int get_head(int u) const { return head[u]; } int get_kth_visited(int k) const { return ord[k]; } int get_subtree_size(int u) const { return siz[u]; } int get_parent(int u) const { return par[u]; } int get_depth(int u) const { return dep[u]; } std::vector<int> get_roots() const { std::vector<int> res; for (int i = 0; i < n; ++i) if (par[i] < 0) res.push_back(i); return res; } private: int n; std::vector<int> visit, leave, head, ord, siz, par, dep; int dfs(Graph &g, int u, int p) { par[u] = p; siz[u] = 1; int max_size = 0; for (int &v : g[u]) { if (v == p) continue; dep[v] = dep[u] + 1; siz[u] += dfs(g, v, u); if (max_size < siz[v]) { max_size = siz[v]; std::swap(g[u].front(), v); } } return siz[u]; } void hld(Graph &g, int u, int p, int &time) { visit[u] = time, ord[time] = u, ++time; head[u] = p >= 0 and g[p].front() == u ? head[p] : u; for (int v : g[u]) { if (v != p) hld(g, v, u, time); } leave[u] = time; } }; } // namespace suisen #line 6 "test/src/tree/heavy_light_decomposition/la_based_lowest_common_ancestor.test.cpp" using suisen::HeavyLightDecomposition; int main() { std::ios::sync_with_stdio(false); std::cin.tie(nullptr); int n, q; std::cin >> n >> q; std::vector<std::vector<int>> g(n); for (int i = 1; i < n; ++i) { int p; std::cin >> p; g[p].push_back(i); g[i].push_back(p); } HeavyLightDecomposition hld(g); auto lca = [&](int u, int v) -> int { int du = hld.get_depth(u); int dv = hld.get_depth(v); if (du < dv) { std::swap(u, v); std::swap(du, dv); } int l = -1, r = dv; while (r - l > 1) { int k = (l + r) >> 1; (hld.la(u, k + (du - dv)) == hld.la(v, k) ? r : l) = k; } return hld.la(v, r); }; for (int query_id = 0; query_id < q; ++query_id) { int u, v; std::cin >> u >> v; std::cout << lca(u, v) << '\n'; } return 0; }