diff --git a/.verify-helper/timestamps.remote.json b/.verify-helper/timestamps.remote.json new file mode 100644 index 00000000..14af3fd2 --- /dev/null +++ b/.verify-helper/timestamps.remote.json @@ -0,0 +1,21 @@ +{ +"tests/library_checker_aizu_tests/convolution/gcd_convolution.test.cpp": "2025-06-25 15:06:18 -0600", +"tests/library_checker_aizu_tests/convolution/lcm_convolution.test.cpp": "2025-06-25 15:06:18 -0600", +"tests/library_checker_aizu_tests/convolution/min_plus_convolution.test.cpp": "2024-11-17 14:04:03 -0600", +"tests/library_checker_aizu_tests/data_structures/binary_search_example.test.cpp": "2024-11-18 10:51:39 -0600", +"tests/library_checker_aizu_tests/data_structures/binary_trie.test.cpp": "2024-11-17 14:04:03 -0600", +"tests/library_checker_aizu_tests/data_structures/bit.test.cpp": "2024-12-14 19:50:29 -0600", +"tests/library_checker_aizu_tests/data_structures/bit_inc.test.cpp": "2024-11-18 09:44:22 -0600", +"tests/library_checker_aizu_tests/data_structures/bit_ordered_set.test.cpp": "2024-12-15 14:34:10 -0600", +"tests/library_checker_aizu_tests/data_structures/bit_rupq.test.cpp": "2024-12-05 10:41:42 -0600", +"tests/library_checker_aizu_tests/data_structures/bit_rurq.test.cpp": "2024-12-05 10:41:42 -0600", +"tests/library_checker_aizu_tests/data_structures/bit_walk.test.cpp": "2024-12-14 19:50:29 -0600", +"tests/library_checker_aizu_tests/data_structures/deque.test.cpp": "2025-06-23 03:43:10 -0600", +"tests/library_checker_aizu_tests/data_structures/deque_op.test.cpp": "2025-06-23 03:43:10 -0600", +"tests/library_checker_aizu_tests/data_structures/deque_sliding_window.test.cpp": "2025-06-23 03:43:10 -0600", +"tests/library_checker_aizu_tests/data_structures/disjoint_rmq_inc.test.cpp": "2024-12-14 15:47:13 -0600", +"tests/library_checker_aizu_tests/data_structures/disjoint_rmq_inc_lines.test.cpp": "2024-12-14 15:47:13 -0600", +"tests/library_checker_aizu_tests/data_structures/disjoint_rmq_lines.test.cpp": "2024-12-14 15:47:13 -0600", +"tests/library_checker_aizu_tests/data_structures/disjoint_rmq_sum.test.cpp": "2024-12-14 15:47:13 -0600", +"tests/library_checker_aizu_tests/data_structures/distinct_query.test.cpp": "2024-12-05 10:41:42 -0600" +} \ No newline at end of file diff --git a/library/trees/ladder_decomposition/linear_kth_par.hpp b/library/trees/ladder_decomposition/linear_kth_par.hpp index 6fde5810..4c68b387 100644 --- a/library/trees/ladder_decomposition/linear_kth_par.hpp +++ b/library/trees/ladder_decomposition/linear_kth_par.hpp @@ -7,62 +7,47 @@ //! } //! vector> adj(n); //! linear_kth_par kp(adj); -//! int kth_par = kp.kth_par(v, k); +//! kp.kth_par(v, k); // k edges up from v +//! kp.kth_par(v, 1); // v's parent //! @endcode -//! kth_par = a node k edges up from v -//! @time O(n + q) -//! @space O(n) -struct linear_kth_par { - struct node { - int d, p = -1, dl, idx_j; - basic_string lad; - }; - vector t; - vector j; +//! @time O(n*max((2*KAPPA+3)/KAPPA,2*KAPPA) + q) +//! @space O(n*max((2*KAPPA+3)/KAPPA,2*KAPPA)) +template struct linear_kth_par { + int n; + vi d, leaf, pos, jmp; + vector lad; linear_kth_par(const auto& adj): - t(sz(adj)), j(2 * sz(t)) { - vi st; - int pos = 1; - auto add_j = [&]() -> void { - j[pos] = { - st[max(0, sz(st) - 1 - 2 * (pos & -pos))], - st[max(0, sz(st) - 1 - 4 * (pos & -pos))]}; - pos++; + n(sz(adj)), d(n), leaf(n), pos(n), jmp(2 * n), lad(n) { + static_assert(KAPPA >= 1); + int t = 1; + vi st(n); + auto calc = [&](int siz) { + jmp[t] = st[max(0, siz - KAPPA * (t & -t))]; + t++; }; - auto dfs = [&](auto&& self, int v) -> void { - st.push_back(v); - t[v].idx_j = pos, t[v].dl = v; - add_j(); + auto dfs = [&](auto&& self, int v, int p) { + st[d[v]] = v; + int& l = leaf[v] = v; + pos[v] = t; + calc(d[v]); for (int u : adj[v]) - if (u != t[v].p) { - t[u].d = t[t[u].p = v].d + 1; - self(self, u); - if (t[t[u].dl].d > t[t[v].dl].d) - t[v].dl = t[u].dl; - add_j(); + if (u != p) { + d[u] = 1 + d[v]; + self(self, u, v); + if (d[l] < d[leaf[u]]) l = leaf[u]; + calc(d[v]); } - st.pop_back(); + int s = (d[l] - d[v]) * (2 * KAPPA + 3) / KAPPA; + s = min(max(s, 2 * KAPPA), d[l] + 1); + rep(i, sz(lad[l]), s) lad[l].push_back(st[d[l] - i]); }; - rep(i, 0, sz(t)) { - if (t[i].p == -1) dfs(dfs, i); - if (t[i].p == -1 || t[t[i].p].dl != t[i].dl) { - int v = t[i].dl, len = (t[v].d - t[i].d) * 2; - auto& lad = t[v].lad; - for (; v != -1 && len--; v = t[v].p) lad += v; - } - } + dfs(dfs, 0, 0); } int kth_par(int v, int k) { - assert(0 <= k && k <= t[v].d); - switch (k) { - case 0: return v; - case 1: return t[v].p; - case 2: return t[t[v].p].p; - default: - int i = bit_floor(unsigned(k / 3)); - auto [j1, j2] = j[(t[v].idx_j & -i) | i]; - int leaf = t[t[v].d - t[j2].d <= k ? j2 : j1].dl; - return t[leaf].lad[k + t[leaf].d - t[v].d]; - } + assert(0 <= k && k <= d[v]); + int j = v; + if (unsigned b = k / (KAPPA + 1); b) + b = bit_floor(b), j = jmp[(pos[v] & -b) | b]; + return j = leaf[j], lad[j][k + d[j] - d[v]]; } }; diff --git a/library/trees/linear_kth_path.hpp b/library/trees/linear_kth_path.hpp deleted file mode 100644 index 29f1277b..00000000 --- a/library/trees/linear_kth_path.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once -#include "linear_lca.hpp" -#include "ladder_decomposition/linear_kth_par.hpp" -struct linear_kth_path { - linear_lca lin_lca; - linear_kth_par lin_kp; - linear_kth_path(const auto& adj): - lin_lca(adj), lin_kp(adj) {} - //! @param u,v endpoint nodes of path - //! @param k index into path - //! @returns the node - //! vi({u,p[u],..,lca(u,v),..,p[v],v})[k], or -1, so u if - //! k=0 - //! @time O(1) - //! @space O(1) - int kth_path(int u, int v, int k) { - int lca_d = lin_kp.t[lin_lca.lca(u, v)].d; - int u_lca = lin_kp.t[u].d - lca_d; - int v_lca = lin_kp.t[v].d - lca_d; - if (k <= u_lca) return lin_kp.kth_par(u, k); - if (k <= u_lca + v_lca) - return lin_kp.kth_par(v, u_lca + v_lca - k); - return -1; - } -}; diff --git a/tests/library_checker_aizu_tests/trees/kth_path_ladder.test.cpp b/tests/library_checker_aizu_tests/trees/kth_path_ladder.test.cpp index e9e0fd8f..55618040 100644 --- a/tests/library_checker_aizu_tests/trees/kth_path_ladder.test.cpp +++ b/tests/library_checker_aizu_tests/trees/kth_path_ladder.test.cpp @@ -16,14 +16,6 @@ int main() { } ladder ld(adj); assert(sz(ld.l_tbl) <= 2 * n - ld.d[0]); - vector> adj_rooted(n + n); - for (int i = 0; i < n; i++) - if (ld.p[i] != i) { - adj_rooted[ld.p[i]].push_back(i); - adj_rooted[ld.p[i] + n].push_back(i + n); - } - ladder ld_rooted(adj_rooted); - linear_kth_par lin_ld_rooted(adj_rooted); while (q--) { int u, v, k; cin >> u >> v >> k; @@ -34,23 +26,10 @@ int main() { else if (k <= u_lca) { int res = ld.kth_par(u, k); assert(res == jmp(ld.b_tbl, u, k)); - assert(res == ld_rooted.kth_par(u, k)); - assert(res == ld_rooted.kth_par(u + n, k) - n); - assert(res == lin_ld_rooted.kth_par(u, k)); - assert(res == lin_ld_rooted.kth_par(u + n, k) - n); cout << res << '\n'; } else { int res = ld.kth_par(v, u_lca + v_lca - k); assert(res == jmp(ld.b_tbl, v, u_lca + v_lca - k)); - assert( - res == ld_rooted.kth_par(v, u_lca + v_lca - k)); - assert(res == - ld_rooted.kth_par(v + n, u_lca + v_lca - k) - n); - assert(res == - lin_ld_rooted.kth_par(v, u_lca + v_lca - k)); - assert(res == - lin_ld_rooted.kth_par(v + n, u_lca + v_lca - k) - - n); cout << res << '\n'; } } diff --git a/tests/library_checker_aizu_tests/trees/kth_path_linear.test.cpp b/tests/library_checker_aizu_tests/trees/kth_path_linear.test.cpp index 40055b99..51aae584 100644 --- a/tests/library_checker_aizu_tests/trees/kth_path_linear.test.cpp +++ b/tests/library_checker_aizu_tests/trees/kth_path_linear.test.cpp @@ -1,7 +1,8 @@ #define PROBLEM \ "https://judge.yosupo.jp/problem/jump_on_tree" #include "../template.hpp" -#include "../../../library/trees/linear_kth_path.hpp" +#include "../../../library/trees/linear_lca.hpp" +#include "../../../library/trees/ladder_decomposition/linear_kth_par.hpp" #include "../../../library/trees/lca_rmq/lca_rmq.hpp" #include "../compress_tree_asserts.hpp" int main() { @@ -15,12 +16,29 @@ int main() { adj[u].push_back(v); adj[v].push_back(u); } - linear_kth_path lin_kth_path(adj); + linear_lca lin_lca(adj); + linear_kth_par<1> lin_kp_1(adj); + linear_kth_par<2> lin_kp_2(adj); + linear_kth_par<3> lin_kp_3(adj); + linear_kth_par<4> lin_kp_4(adj); LCA lc(adj); compress_tree_asserts(adj, lc); + auto get_kth_par = [&](int v, int k) -> int { + int res = lin_kp_1.kth_par(v, k); + assert(res == lin_kp_2.kth_par(v, k)); + assert(res == lin_kp_3.kth_par(v, k)); + assert(res == lin_kp_4.kth_par(v, k)); + return res; + }; while (q--) { int u, v, k; cin >> u >> v >> k; - cout << lin_kth_path.kth_path(u, v, k) << '\n'; + int lca_d = lin_kp_2.d[lin_lca.lca(u, v)]; + int u_lca = lin_kp_2.d[u] - lca_d; + int v_lca = lin_kp_2.d[v] - lca_d; + if (k <= u_lca) cout << get_kth_par(u, k) << '\n'; + else if (k <= u_lca + v_lca) + cout << get_kth_par(v, u_lca + v_lca - k) << '\n'; + else cout << -1 << '\n'; } }