diff --git a/library/data_structures/dsu/dsu_bipartite.hpp b/library/data_structures/dsu/dsu_bipartite.hpp index 1c1402d0..8cbb199e 100644 --- a/library/data_structures/dsu/dsu_bipartite.hpp +++ b/library/data_structures/dsu/dsu_bipartite.hpp @@ -4,37 +4,33 @@ //! bipartite check struct dsu_bipartite { int num_sets; - struct node { - int p = -1; - bool is_bi = 1, parity; - }; - vector t; - dsu_bipartite(int n): num_sets(n), t(n) {} + vi p, is_bi, parity; + dsu_bipartite(int n): + num_sets(n), p(n, -1), is_bi(n, 1), parity(n) {} int find(int v) { - if (t[v].p < 0) return v; - int root = find(t[v].p); - t[v].parity ^= t[t[v].p].parity; - return t[v].p = root; + if (p[v] < 0) return v; + int root = find(p[v]); + parity[v] ^= parity[p[v]]; + return p[v] = root; } bool join(int u, int v) { int root_u = find(u), root_v = find(v); if (root_u == root_v) { - if (t[u].parity == t[v].parity) t[root_u].is_bi = 0; + if (parity[u] == parity[v]) is_bi[root_u] = 0; return 0; } - if (t[root_u].p > t[root_v].p) { + if (p[root_u] > p[root_v]) { swap(u, v); swap(root_u, root_v); } - t[root_u].is_bi &= t[root_v].is_bi; - t[root_v].parity = t[v].parity ^ 1 ^ t[u].parity; - t[root_u].p += t[root_v].p, t[root_v].p = root_u, - num_sets--; + is_bi[root_u] &= is_bi[root_v]; + parity[root_v] = parity[v] ^ 1 ^ parity[u]; + p[root_u] += p[root_v], p[root_v] = root_u, num_sets--; return 1; } - int size(int v) { return -t[find(v)].p; } + int size(int v) { return -p[find(v)]; } bool same_set(int u, int v) { return find(u) == find(v); } - bool is_bipartite(int v) { return t[find(v)].is_bi; } + bool is_bipartite(int v) { return is_bi[find(v)]; } }; diff --git a/library/trees/centroid_decomp.hpp b/library/trees/centroid_decomp.hpp index 313cde12..2429168e 100644 --- a/library/trees/centroid_decomp.hpp +++ b/library/trees/centroid_decomp.hpp @@ -18,7 +18,7 @@ template struct centroid { vi siz; centroid(const G& adj, F f): adj(adj), f(f), siz(sz(adj), -1) { - rep(i, 0, sz(adj)) if (siz[i] == -1) dfs(i, -1); + dfs(0, -1); } void calc_sz(int v, int p) { siz[v] = 1; diff --git a/library/trees/extra_members/compress_tree.hpp b/library/trees/extra_members/compress_tree.hpp index 182e4c4d..a956f305 100644 --- a/library/trees/extra_members/compress_tree.hpp +++ b/library/trees/extra_members/compress_tree.hpp @@ -11,10 +11,10 @@ //! @time O(|subset| log |subset|) //! @space O(|subset|) array compress_tree(vi subset) { - auto proj = [&](int v) { return t[v].in; }; + auto proj = [&](int v) { return in[v]; }; ranges::sort(subset, {}, proj); - int siz = sz(subset); - rep(i, 1, siz) + int len = sz(subset); + rep(i, 1, len) subset.push_back(lca(subset[i - 1], subset[i])); ranges::sort(subset, {}, proj); subset.erase(unique(all(subset)), end(subset)); diff --git a/library/trees/extra_members/dist_edges.hpp b/library/trees/extra_members/dist_edges.hpp index 0e776186..bc41b9fb 100644 --- a/library/trees/extra_members/dist_edges.hpp +++ b/library/trees/extra_members/dist_edges.hpp @@ -1,3 +1,3 @@ -int dist_edges(int u, int v) { - return t[u].d + t[v].d - 2 * t[lca(u, v)].d; +int dist(int u, int v) { + return d[u] + d[v] - 2 * d[lca(u, v)]; } diff --git a/library/trees/extra_members/in_subtree.hpp b/library/trees/extra_members/in_subtree.hpp index d8e5834a..bec278ca 100644 --- a/library/trees/extra_members/in_subtree.hpp +++ b/library/trees/extra_members/in_subtree.hpp @@ -1,5 +1,4 @@ //! returns 1 if v is in u's subtree bool in_subtree(int u, int v) { - return t[u].in <= t[v].in && - t[v].in < t[u].in + t[u].sub_sz; + return in[u] <= in[v] && in[v] < in[u] + siz[u]; } diff --git a/library/trees/ladder_decomposition/ladder_decomposition.hpp b/library/trees/ladder_decomposition/ladder_decomposition.hpp index 1cfca41e..945d44aa 100644 --- a/library/trees/ladder_decomposition/ladder_decomposition.hpp +++ b/library/trees/ladder_decomposition/ladder_decomposition.hpp @@ -5,37 +5,36 @@ //! @code //! ladder ld(adj); //! // KACTL functions -//! int kth_par = jmp(ld.b_tbl, v, k); -//! int curr_lca = lca(ld.b_tbl, ld.d, u, v); +//! int kth_par = jmp(ld.jmp, v, k); +//! int curr_lca = lca(ld.jmp, ld.d, u, v); //! @endcode struct ladder { int n; - vector b_tbl; - vi d, p, dl, idx_l, l_tbl; + vi d, p, leaf, idx, lad; + vector jmp; //! @param adj forest (rooted or unrooted) //! @time O(n log n) - //! @space O(n log n) for b_tbl. Everything else is O(n) + //! @space O(n log n) for jmp. Everything else is O(n) ladder(const auto& adj): - n(sz(adj)), d(n), p(n, -1), dl(n), idx_l(n) { + n(sz(adj)), d(n), p(n), leaf(n), idx(n), lad(2 * n) { auto dfs = [&](auto&& self, int v) -> void { - dl[v] = v; + leaf[v] = v; for (int u : adj[v]) if (u != p[v]) { d[u] = d[p[u] = v] + 1; self(self, u); - if (d[dl[u]] > d[dl[v]]) dl[v] = dl[u]; + if (d[leaf[v]] < d[leaf[u]]) leaf[v] = leaf[u]; } }; - rep(i, 0, n) { - if (p[i] == -1) p[i] = i, dfs(dfs, i); - if (p[i] == i || dl[p[i]] != dl[i]) { - int v = dl[i], len = (d[v] - d[i]) * 2; - idx_l[v] = sz(l_tbl) + d[v]; - for (; v != -1 && len--; v = p[v]) - l_tbl.push_back(v); - } + dfs(dfs, 0); + int pos = 0; + rep(i, 0, n) if (p[i] == i || leaf[p[i]] != leaf[i]) { + int l = leaf[i]; + int len = min((d[l] - d[i]) * 2, d[l] + 1); + idx[l] = pos; + for (; len--; l = p[l]) lad[pos++] = l; } - b_tbl = treeJump(p); + jmp = treeJump(p); } //! @param v query node //! @param k number of edges @@ -47,11 +46,11 @@ struct ladder { assert(0 <= k && k <= d[v]); if (k == 0) return v; int bit = __lg(k); - v = b_tbl[bit][v], k -= (1 << bit); - int l = idx_l[dl[v]] - d[v]; - assert(l_tbl[l] == v); - // subarray [l, l+k] of l_tbl corresponds to the rest + v = jmp[bit][v], k -= (1 << bit); + int l = idx[leaf[v]] + d[leaf[v]] - d[v]; + assert(lad[l] == v); + // subarray [l, l+k] of lad corresponds to the rest // of the jump - return l_tbl[l + k]; + return lad[l + k]; } }; diff --git a/library/trees/lca_rmq/lca_rmq.hpp b/library/trees/lca_rmq/lca_rmq.hpp index c445e963..38e7150b 100644 --- a/library/trees/lca_rmq/lca_rmq.hpp +++ b/library/trees/lca_rmq/lca_rmq.hpp @@ -13,29 +13,27 @@ //! @space O(nlogn) // NOLINTNEXTLINE(readability-identifier-naming) struct LCA { - struct node { - int in, sub_sz = 1, d, p = -1; - }; - vector t; + int n; + vi in, siz, d, p; RMQ> rmq = {{}, NULL}; - LCA(const auto& adj): t(sz(adj)) { + LCA(const auto& adj): + n(sz(adj)), in(n), siz(n, 1), d(n), p(n) { vi order; auto dfs = [&](auto&& self, int v) -> void { - t[v].in = sz(order), order.push_back(v); + in[v] = sz(order), order.push_back(v); for (int u : adj[v]) - if (u != t[v].p) - t[u].d = t[t[u].p = v].d + 1, self(self, u), - t[v].sub_sz += t[u].sub_sz; + if (u != p[v]) + d[u] = d[p[u] = v] + 1, self(self, u), + siz[v] += siz[u]; }; - rep(i, 0, sz(t)) if (t[i].p == -1) dfs(dfs, i); - rmq = {order, [&](int u, int v) { - return t[u].d < t[v].d ? u : v; - }}; + dfs(dfs, 0); + rmq = {order, + [&](int u, int v) { return d[u] < d[v] ? u : v; }}; } int lca(int u, int v) { if (u == v) return u; - auto [x, y] = minmax(t[u].in, t[v].in); - return t[rmq.query(x + 1, y + 1)].p; + auto [x, y] = minmax(in[u], in[v]); + return p[rmq.query(x + 1, y + 1)]; } #include "../extra_members/dist_edges.hpp" #include "../extra_members/in_subtree.hpp" diff --git a/library/trees/lca_rmq/next_on_path.hpp b/library/trees/lca_rmq/next_on_path.hpp index d194fb88..9d307517 100644 --- a/library/trees/lca_rmq/next_on_path.hpp +++ b/library/trees/lca_rmq/next_on_path.hpp @@ -5,7 +5,6 @@ //! @space O(1) int next_on_path(int u, int v) { assert(u != v); - return in_subtree(u, v) - ? rmq.query(t[u].in + 1, t[v].in + 1) - : t[u].p; + return in_subtree(u, v) ? rmq.query(in[u] + 1, in[v] + 1) + : p[u]; } diff --git a/library/trees/linear_lca.hpp b/library/trees/linear_lca.hpp index e36bc666..853d40dd 100644 --- a/library/trees/linear_lca.hpp +++ b/library/trees/linear_lca.hpp @@ -41,4 +41,5 @@ struct linear_lca { } return d[u] < d[v] ? u : v; } +#include "extra_members/dist_edges.hpp" }; diff --git a/library/trees/subtree_isomorphism.hpp b/library/trees/subtree_isomorphism.hpp index c50f1e0d..708604a4 100644 --- a/library/trees/subtree_isomorphism.hpp +++ b/library/trees/subtree_isomorphism.hpp @@ -26,6 +26,6 @@ auto subtree_iso(const auto& adj) { hashes.try_emplace(ch_ids, sz(hashes)) .first->second; }; - rep(i, 0, sz(adj)) if (iso_id[i] == -1) dfs(dfs, i, i); + dfs(dfs, 0, 0); return pair{sz(hashes), iso_id}; } diff --git a/library/trees/tree_lift/kth_path.hpp b/library/trees/tree_lift/kth_path.hpp deleted file mode 100644 index 709ba0ef..00000000 --- a/library/trees/tree_lift/kth_path.hpp +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once -//! returns the node vi({u,p[u],..,lca(u,v),..,p[v],v})[k] -//! @time same as lca(u, v), kth_par(u, k) -//! @space O(1) -int kth_path(int u, int v, int k) { - int lca_d = t[lca(u, v)].d; - int u_lca = t[u].d - lca_d; - int v_lca = t[v].d - lca_d; - if (k <= u_lca) return kth_par(u, k); - if (k <= u_lca + v_lca) - return kth_par(v, u_lca + v_lca - k); - return -1; -} diff --git a/library/trees/tree_lift/tree_lift.hpp b/library/trees/tree_lift/tree_lift.hpp index b2083eba..f3b0ceae 100644 --- a/library/trees/tree_lift/tree_lift.hpp +++ b/library/trees/tree_lift/tree_lift.hpp @@ -7,44 +7,35 @@ //! } //! vector> adj(n); //! tree_lift tree_l(adj); -//! int kth_p = tree_l.kth_par(v, k); +//! tree_l.kth_par(v, k); // k edges up from v //! @endcode -//! kth_p = a node k edges up from v //! @time O(n + q log n) //! @space O(n) struct tree_lift { - struct node { - int d, p = -1, j = -1; - }; - vector t; - tree_lift(const auto& adj): t(sz(adj)) { + vi d, p, j; + tree_lift(const auto& adj): d(sz(adj)), p(d), j(d) { auto dfs = [&](auto&& self, int v) -> void { - int jump = - (t[v].d + t[t[t[v].j].j].d == 2 * t[t[v].j].d) - ? t[t[v].j].j - : v; + int up = + d[v] + d[j[j[v]]] == 2 * d[j[v]] ? j[j[v]] : v; for (int u : adj[v]) - if (u != t[v].p) - t[u].d = t[t[u].p = v].d + 1, t[u].j = jump, - self(self, u); + if (u != p[v]) + d[u] = d[p[u] = v] + 1, j[u] = up, self(self, u); }; - rep(i, 0, sz(t)) if (t[i].j == -1) t[i].j = i, - dfs(dfs, i); + dfs(dfs, 0); } int kth_par(int v, int k) { - int anc_d = t[v].d - k; - while (t[v].d > anc_d) - v = t[t[v].j].d >= anc_d ? t[v].j : t[v].p; + int anc_d = d[v] - k; + while (d[v] > anc_d) + v = d[j[v]] >= anc_d ? j[v] : p[v]; return v; } int lca(int u, int v) { - if (t[u].d < t[v].d) swap(u, v); - u = kth_par(u, t[u].d - t[v].d); + if (d[u] < d[v]) swap(u, v); + u = kth_par(u, d[u] - d[v]); while (u != v) - if (t[u].j != t[v].j) u = t[u].j, v = t[v].j; - else u = t[u].p, v = t[v].p; + if (j[u] != j[v]) u = j[u], v = j[v]; + else u = p[u], v = p[v]; return u; } #include "../extra_members/dist_edges.hpp" -#include "kth_path.hpp" }; diff --git a/tests/library_checker_aizu_tests/handmade_tests/count_paths_forest.test.cpp b/tests/library_checker_aizu_tests/handmade_tests/count_paths.test.cpp similarity index 59% rename from tests/library_checker_aizu_tests/handmade_tests/count_paths_forest.test.cpp rename to tests/library_checker_aizu_tests/handmade_tests/count_paths.test.cpp index b099d875..c5176986 100644 --- a/tests/library_checker_aizu_tests/handmade_tests/count_paths_forest.test.cpp +++ b/tests/library_checker_aizu_tests/handmade_tests/count_paths.test.cpp @@ -3,23 +3,20 @@ #include "../template.hpp" #include "../../../library/contest/random.hpp" #include "../../../library/trees/centroid_decomp_uncommon/count_paths_per_node.hpp" -#include "../../../library/data_structures/dsu/dsu_restorable.hpp" -#include "../../../library/trees/tree_lift/tree_lift.hpp" +#include "../../../library/trees/lca_rmq/lca_rmq.hpp" #include "../../../library/trees/centroid_decomp_uncommon/count_paths_per_length.hpp" #include "../cd_asserts.hpp" -vector> naive(const vector>& adj, - dsu_restorable& dsu) { - tree_lift tl(adj); +vector> naive(const vector& adj) { + LCA lc(adj); int n = sz(adj); vector> cnts_naive(n + 1, vector(n, 0)); for (int u = 0; u < n; u++) { for (int v = u; v < n; v++) { - if (dsu.same_set(u, v)) { - int path_length_edges = tl.dist_edges(u, v); - for (int i = 0; i <= path_length_edges; i++) - cnts_naive[path_length_edges] - [tl.kth_path(u, v, i)]++; - } + int path_length_edges = lc.dist(u, v); + for (int node = u; node != v; + node = lc.next_on_path(node, v)) + cnts_naive[path_length_edges][node]++; + cnts_naive[path_length_edges][v]++; } } return cnts_naive; @@ -27,19 +24,14 @@ vector> naive(const vector>& adj, int main() { cin.tie(0)->sync_with_stdio(0); for (int n = 1; n <= 100; n++) { - vector> adj(n); - dsu_restorable dsu(n); - for (int i = 0; i < n - 2; i++) { - int u = rnd(0, n - 1); - int v = rnd(0, n - 1); - if (u == v) continue; - if (dsu.join(u, v)) { - adj[u].push_back(v); - adj[v].push_back(u); - } + vector adj(n); + for (int i = 1; i < n; i++) { + int par = rnd(0, i - 1); + adj[par].push_back(i); + adj[i].push_back(par); } cd_asserts(adj); - vector> cnts_naive = naive(adj, dsu); + vector> cnts_naive = naive(adj); for (int k = 1; k <= n; k++) assert( count_paths_per_node(adj, k) == cnts_naive[k]); diff --git a/tests/library_checker_aizu_tests/handmade_tests/lca_ladder_forest.test.cpp b/tests/library_checker_aizu_tests/handmade_tests/lca_ladder_forest.test.cpp deleted file mode 100644 index 1878ba5b..00000000 --- a/tests/library_checker_aizu_tests/handmade_tests/lca_ladder_forest.test.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#define PROBLEM \ - "https://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=ITP1_1_A" -#include "../template.hpp" -#include "../../../library/contest/random.hpp" -#include "../../../library/data_structures/dsu/dsu_restorable.hpp" -#include "../../../library/monotonic_stack/monotonic_stack.hpp" -#include "../../../library/trees/tree_lift/tree_lift.hpp" -#include "../../../library/trees/lca_rmq/lca_rmq.hpp" -#include "../../../library/trees/ladder_decomposition/ladder_decomposition.hpp" -#include "../../../library/trees/ladder_decomposition/linear_kth_par.hpp" -int main() { - cin.tie(0)->sync_with_stdio(0); - for (int n = 1; n <= 100; n++) { - vector> adj(n); - dsu_restorable dsu(n); - for (int i = 0; i < n - 2; i++) { - int u = rnd(0, n - 1); - int v = rnd(0, n - 1); - if (u == v) continue; - if (dsu.join(u, v)) { - adj[u].push_back(v); - adj[v].push_back(u); - } - } - tree_lift tl(adj); - LCA lc(adj); - ladder lad(adj); - for (int i = 0; i < 100; i++) { - int u = rnd(0, n - 1); - int v = rnd(0, n - 1); - if (u == v || !dsu.same_set(u, v)) continue; - auto lca_1 = tl.lca(u, v); - auto lca_2 = lc.lca(u, v); - assert(lca_1 == lca_2); - assert(tl.dist_edges(u, v) == lc.dist_edges(u, v)); - assert( - tl.kth_path(u, v, 1) == lc.next_on_path(u, v)); - if (tl.t[u].d > tl.t[v].d) swap(u, v); - auto res = tl.kth_par(v, tl.t[v].d - tl.t[u].d); - assert(res == lad.kth_par(v, tl.t[v].d - tl.t[u].d)); - assert((u == res) == lc.in_subtree(u, v)); - } - } - cout << "Hello World\n"; - return 0; -} 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 55618040..7df79a15 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 @@ -15,21 +15,20 @@ int main() { adj[v].push_back(u); } ladder ld(adj); - assert(sz(ld.l_tbl) <= 2 * n - ld.d[0]); while (q--) { int u, v, k; cin >> u >> v >> k; - int lca_d = ld.d[lca(ld.b_tbl, ld.d, u, v)]; + int lca_d = ld.d[lca(ld.jmp, ld.d, u, v)]; int u_lca = ld.d[u] - lca_d; int v_lca = ld.d[v] - lca_d; if (k > u_lca + v_lca) cout << -1 << '\n'; else if (k <= u_lca) { int res = ld.kth_par(u, k); - assert(res == jmp(ld.b_tbl, u, k)); + assert(res == jmp(ld.jmp, u, k)); 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 == jmp(ld.jmp, v, u_lca + v_lca - k)); cout << res << '\n'; } } diff --git a/tests/library_checker_aizu_tests/trees/kth_path_tree_lift.test.cpp b/tests/library_checker_aizu_tests/trees/kth_path_tree_lift.test.cpp index 376782a2..35ea2066 100644 --- a/tests/library_checker_aizu_tests/trees/kth_path_tree_lift.test.cpp +++ b/tests/library_checker_aizu_tests/trees/kth_path_tree_lift.test.cpp @@ -17,22 +17,29 @@ int main() { } tree_lift tl(adj); LCA lc(adj); + auto kth_path = [&](int u, int v, int k) -> int { + int lca_d = lc.d[lc.lca(u, v)]; + int u_lca = lc.d[u] - lca_d; + int v_lca = lc.d[v] - lca_d; + if (k <= u_lca) return tl.kth_par(u, k); + if (k <= u_lca + v_lca) + return tl.kth_par(v, u_lca + v_lca - k); + return -1; + }; while (q--) { int u, v, k; cin >> u >> v >> k; - int dist_in_edges = tl.dist_edges(u, v); - assert(dist_in_edges == lc.dist_edges(u, v)); - cout << tl.kth_path(u, v, k) << '\n'; + int dist_in_edges = tl.dist(u, v); + assert(dist_in_edges == lc.dist(u, v)); + cout << kth_path(u, v, k) << '\n'; { int w = rnd(0, n - 1); assert(lc.on_path(u, v, w) == - (lc.dist_edges(u, w) + lc.dist_edges(w, v) == - lc.dist_edges(u, v))); + (lc.dist(u, w) + lc.dist(w, v) == lc.dist(u, v))); } if (u != v) { - assert( - tl.kth_path(u, v, 1) == lc.next_on_path(u, v)); - assert(tl.kth_path(u, v, dist_in_edges - 1) == + assert(kth_path(u, v, 1) == lc.next_on_path(u, v)); + assert(kth_path(u, v, dist_in_edges - 1) == lc.next_on_path(v, u)); } } diff --git a/tests/library_checker_aizu_tests/trees/lca_all_methods_aizu.test.cpp b/tests/library_checker_aizu_tests/trees/lca_all_methods_aizu.test.cpp index f914f11b..4a6ac9de 100644 --- a/tests/library_checker_aizu_tests/trees/lca_all_methods_aizu.test.cpp +++ b/tests/library_checker_aizu_tests/trees/lca_all_methods_aizu.test.cpp @@ -25,8 +25,8 @@ int main() { assert(tl.lca(i, i) == i); assert(lc.lca(i, i) == i); assert(lin_lca.lca(i, i) == i); - assert(lc.t[lc.rmq.dp[0][i]].in == i && - lc.rmq.dp[0][lc.t[i].in] == i); + assert(lc.in[lc.rmq.dp[0][i]] == i && + lc.rmq.dp[0][lc.in[i]] == i); } int q; cin >> q; diff --git a/tests/library_checker_aizu_tests/trees/lca_all_methods_lib_checker.test.cpp b/tests/library_checker_aizu_tests/trees/lca_all_methods_lib_checker.test.cpp index 3e65cbe2..454a8dde 100644 --- a/tests/library_checker_aizu_tests/trees/lca_all_methods_lib_checker.test.cpp +++ b/tests/library_checker_aizu_tests/trees/lca_all_methods_lib_checker.test.cpp @@ -24,8 +24,8 @@ int main() { assert(lc.lca(i, i) == i); assert(lc.in_subtree(i, i)); assert(lin_lca.lca(i, i) == i); - assert(lc.t[lc.rmq.dp[0][i]].in == i && - lc.rmq.dp[0][lc.t[i].in] == i); + assert(lc.in[lc.rmq.dp[0][i]] == i && + lc.rmq.dp[0][lc.in[i]] == i); } while (q--) { int u, v;