diff --git a/.verify-helper/timestamps.remote.json b/.verify-helper/timestamps.remote.json index ca5b0c1e..94a38dc0 100644 --- a/.verify-helper/timestamps.remote.json +++ b/.verify-helper/timestamps.remote.json @@ -18,26 +18,20 @@ "tests/library_checker_aizu_tests/data_structures/disjoint_rmq_inc_lines.test.cpp": "2026-01-18 11:15:41 +0000", "tests/library_checker_aizu_tests/data_structures/disjoint_rmq_inc_sum.test.cpp": "2026-01-18 11:15:41 +0000", "tests/library_checker_aizu_tests/data_structures/distinct_query.test.cpp": "2026-01-18 11:15:41 +0000", -"tests/library_checker_aizu_tests/data_structures/dsu.test.cpp": "2026-01-22 10:08:22 -0700", -"tests/library_checker_aizu_tests/data_structures/dsu_bipartite.test.cpp": "2026-01-18 02:20:40 +0000", "tests/library_checker_aizu_tests/data_structures/implicit_seg_tree.test.cpp": "2026-01-18 11:15:41 +0000", "tests/library_checker_aizu_tests/data_structures/kd_bit_1d.test.cpp": "2026-02-19 18:06:52 -0700", -"tests/library_checker_aizu_tests/data_structures/kruskal_tree_aizu.test.cpp": "2026-01-18 02:20:40 +0000", "tests/library_checker_aizu_tests/data_structures/kth_smallest_pst.test.cpp": "2026-01-18 11:15:41 +0000", "tests/library_checker_aizu_tests/data_structures/kth_smallest_wavelet_matrix.test.cpp": "2026-02-04 02:50:53 +0000", "tests/library_checker_aizu_tests/data_structures/lazy_segment_tree.test.cpp": "2026-01-23 04:31:29 +0000", "tests/library_checker_aizu_tests/data_structures/lazy_segment_tree_constructor.test.cpp": "2026-01-23 04:31:29 +0000", "tests/library_checker_aizu_tests/data_structures/lazy_segment_tree_inc.test.cpp": "2026-02-05 13:02:29 -0700", "tests/library_checker_aizu_tests/data_structures/lazy_segment_tree_inc_constructor.test.cpp": "2026-02-05 13:02:29 -0700", -"tests/library_checker_aizu_tests/data_structures/line_tree_aizu.test.cpp": "2026-01-18 11:15:41 +0000", -"tests/library_checker_aizu_tests/data_structures/line_tree_lib_checker.test.cpp": "2026-01-18 02:20:40 +0000", "tests/library_checker_aizu_tests/data_structures/merge_sort_tree.test.cpp": "2026-01-18 11:04:58 +0000", "tests/library_checker_aizu_tests/data_structures/mode_query.test.cpp": "2026-01-18 02:20:40 +0000", "tests/library_checker_aizu_tests/data_structures/permutation_tree.test.cpp": "2026-01-18 11:15:41 +0000", "tests/library_checker_aizu_tests/data_structures/persistent_queue_tree.test.cpp": "2026-01-18 11:15:41 +0000", "tests/library_checker_aizu_tests/data_structures/persistent_seg_tree.test.cpp": "2026-01-18 11:15:41 +0000", "tests/library_checker_aizu_tests/data_structures/pq_ds_undo_sliding_window.test.cpp": "2026-01-18 02:20:40 +0000", -"tests/library_checker_aizu_tests/data_structures/range_parallel_dsu.test.cpp": "2026-01-22 10:08:22 -0700", "tests/library_checker_aizu_tests/data_structures/rmq_linear.test.cpp": "2026-01-18 11:15:41 +0000", "tests/library_checker_aizu_tests/data_structures/rmq_sparse_table.test.cpp": "2026-01-18 11:15:41 +0000", "tests/library_checker_aizu_tests/data_structures/rmq_sparse_table_inc.test.cpp": "2026-01-18 11:15:41 +0000", @@ -47,6 +41,12 @@ "tests/library_checker_aizu_tests/data_structures/simple_tree_inc_walk.test.cpp": "2026-02-16 16:56:58 -0700", "tests/library_checker_aizu_tests/data_structures/simple_tree_line.test.cpp": "2026-02-14 20:58:21 +0000", "tests/library_checker_aizu_tests/data_structures/simple_tree_walk.test.cpp": "2026-02-14 20:58:21 +0000", +"tests/library_checker_aizu_tests/dsu/dsu.test.cpp": "2026-02-27 11:50:42 -0700", +"tests/library_checker_aizu_tests/dsu/dsu_bipartite.test.cpp": "2026-02-27 11:58:10 -0700", +"tests/library_checker_aizu_tests/dsu/kruskal_tree_aizu.test.cpp": "2026-02-27 11:50:42 -0700", +"tests/library_checker_aizu_tests/dsu/line_tree_aizu.test.cpp": "2026-02-27 11:56:18 -0700", +"tests/library_checker_aizu_tests/dsu/line_tree_lib_checker.test.cpp": "2026-02-27 11:56:18 -0700", +"tests/library_checker_aizu_tests/dsu/range_parallel_dsu.test.cpp": "2026-02-27 14:31:41 -0700", "tests/library_checker_aizu_tests/flow/dinic_aizu.test.cpp": "2024-11-17 14:04:03 -0600", "tests/library_checker_aizu_tests/flow/hungarian.test.cpp": "2024-11-17 14:04:03 -0600", "tests/library_checker_aizu_tests/flow/min_cost_max_flow.test.cpp": "2024-12-05 10:41:42 -0600", diff --git a/library/dsu/dsu_bipartite.hpp b/library/dsu/dsu_bipartite.hpp index 8cbb199e..f7009587 100644 --- a/library/dsu/dsu_bipartite.hpp +++ b/library/dsu/dsu_bipartite.hpp @@ -3,34 +3,28 @@ //! DSU with support for parity of path to root for online //! bipartite check struct dsu_bipartite { - int num_sets; 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) { + dsu_bipartite(int n): p(n, -1), is_bi(n, 1), parity(n) {} + int f(int v) { if (p[v] < 0) return v; - int root = find(p[v]); + int root = f(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 (parity[u] == parity[v]) is_bi[root_u] = 0; + int root_u = f(u), root_v = f(v); + int new_parity = parity[v] ^ parity[u]; + u = root_u, v = root_v; + if (u == v) { + is_bi[u] &= new_parity; return 0; } - if (p[root_u] > p[root_v]) { - swap(u, v); - swap(root_u, root_v); - } - 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--; + if (p[u] > p[v]) swap(u, v); + is_bi[u] &= is_bi[v]; + parity[v] = new_parity ^ 1; + p[u] += p[v], p[v] = u; return 1; } - 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 is_bi[find(v)]; } + int size(int v) { return -p[f(v)]; } + bool is_bipartite(int v) { return is_bi[f(v)]; } }; diff --git a/library/dsu/kruskal_tree.hpp b/library/dsu/kruskal_tree.hpp index 745517c2..d4e7890f 100644 --- a/library/dsu/kruskal_tree.hpp +++ b/library/dsu/kruskal_tree.hpp @@ -7,11 +7,9 @@ struct kr_tree { vi p; vector adj; kr_tree(int n): id(n), p(2 * n, -1), adj(2 * n) {} - int find(int v) { - return p[v] < 0 ? v : p[v] = find(p[v]); - } + int f(int v) { return p[v] < 0 ? v : p[v] = f(p[v]); } bool join(int u, int v) { - if ((u = find(u)) == (v = find(v))) return 0; + if ((u = f(u)) == (v = f(v))) return 0; return adj[p[u] = p[v] = id++] = {u, v}, 1; } }; diff --git a/library/dsu/line_tree.hpp b/library/dsu/line_tree.hpp index 11cc34be..7093a7b7 100644 --- a/library/dsu/line_tree.hpp +++ b/library/dsu/line_tree.hpp @@ -4,14 +4,14 @@ //! ranges::sort(w_eds); //! line_tree lt(n); //! for (auto [w, u, v] : w_eds) lt.join(u, v); -//! for (int v = lt.find(0); v != -1;) { +//! for (int v = lt.f(0); v != -1;) { //! auto [next, e_id] = lt.edge[v]; //! int w = w_eds[e_id][0]; //! // //! v = next; //! } //! @endcode -//! lt.find(v) = head of linked list +//! lt.f(v) = head of linked list //! of component containing v //! @time O(n + m * \alpha(n)) //! @space O(n + m) @@ -22,16 +22,14 @@ struct line_tree { line_tree(int n): p(n, -1), last(n), edge(n, {-1, -1}) { iota(all(last), 0); } - int size(int v) { return -p[find(v)]; } - int find(int v) { - return p[v] < 0 ? v : p[v] = find(p[v]); - } + int size(int v) { return -p[f(v)]; } + int f(int v) { return p[v] < 0 ? v : p[v] = f(p[v]); } bool join(int u, int v) { - u = find(u), v = find(v), id++; + u = f(u), v = f(v), id++; if (u == v) return 0; if (p[u] < p[v]) swap(u, v); p[v] += p[u], p[u] = v; - edge[exchange(last[v], last[u])] = {u, id - 1}; + edge[last[v]] = {u, id - 1}, last[v] = last[u]; return 1; } }; diff --git a/library/dsu/range_parallel_dsu.hpp b/library/dsu/range_parallel_dsu.hpp index 0a10ec58..03bd3d4f 100644 --- a/library/dsu/range_parallel_dsu.hpp +++ b/library/dsu/range_parallel_dsu.hpp @@ -1,31 +1,23 @@ #pragma once #include "dsu.hpp" -//! Given l1,l2,len; joins (l1,l2), (l1+1,l2+1), -//! ..., (l1+len-1,l2+len-1) +//! Given u,v,len; joins (u,v), (u+1,v+1), +//! ..., (u+len-1,v+len-1) //! `f` is called at most n-1 times //! @time O(n*log(n)*\alpha(n) + q) //! @space O(n log n) struct rp_dsu { vector dsus; - rp_dsu(int n): dsus(bit_width(unsigned(n)), DSU(n)) {} - void join(int l1, int l2, int len, const auto& f) { - if (len == 0) return; - int lg = __lg(len); - join_impl(lg, l1, l2, f); - join_impl(lg, l1 + len - (1 << lg), - l2 + len - (1 << lg), f); + rp_dsu(int n): dsus(bit_width(n + 0u), DSU(n)) {} + void join(int u, int v, int len, const auto& f) { + int i = __lg(len); + join(u, v, f, i); + join(u + len - (1 << i), v + len - (1 << i), f, i); } - void join_impl(int lvl, int u, int v, const auto& f) { - if (lvl == 0) { - u = dsus[0].f(u); - v = dsus[0].f(v); - if (!dsus[0].join(v, u)) return; - int w = dsus[0].f(u); - return f(w, u ^ v ^ w); - } - if (!dsus[lvl].join(u, v)) return; - join_impl(lvl - 1, u, v, f); - join_impl(lvl - 1, u + (1 << (lvl - 1)), - v + (1 << (lvl - 1)), f); + void join(int u, int v, const auto& f, int i) { + if (!dsus[i].join(u, v)) return; + if (i == 0) return f(u, v); + i--; + join(u, v, f, i); + join(u + (1 << i), v + (1 << i), f, i); } }; diff --git a/library/dsu/range_parallel_equivalence_classes.hpp b/library/dsu/range_parallel_equivalence_classes.hpp index 3a5796eb..fdbcf1c0 100644 --- a/library/dsu/range_parallel_equivalence_classes.hpp +++ b/library/dsu/range_parallel_equivalence_classes.hpp @@ -1,17 +1,23 @@ #pragma once #include "dsu.hpp" -//! Given triplets (l1,l2,len); joins (l1,l2), -//! (l1+1,l2+1), ..., (l1+len-1,l2+len-1) +//! @code +//! vector> joins(n + 1); +//! joins[len].emplace_back(u, v); +//! // it does: +//! // dsu.join(u + 0, v + 0); +//! // dsu.join(u + 1, v + 1); +//! // dsu.join(u + 2, v + 2); +//! // ... +//! // dsu.join(u + len - 1, v + len - 1); +//! DSU dsu = get_rp_dsu(joins, n); +//! @endcode //! @time O((n + q) * \alpha(n)) //! @space O(n + q) -DSU get_rp_dsu(const vector>& rests, int n) { - vector> rests_by_len(n + 1); - for (auto [l1, l2, len] : rests) - rests_by_len[len].emplace_back(l1, l2); +DSU get_rp_dsu(vector>& joins, int n) { DSU dsu(n); - for (int len = n; len > 0; len--) - for (auto [l1, l2] : rests_by_len[len]) - if (dsu.join(l1, l2)) - rests_by_len[len - 1].emplace_back(l1 + 1, l2 + 1); + for (int i = n; i; i--) + for (auto [u, v] : joins[i]) + if (dsu.join(u, v)) + joins[i - 1].emplace_back(u + 1, v + 1); return dsu; } diff --git a/tests/.config/.cppcheck_suppression_list b/tests/.config/.cppcheck_suppression_list index 788b33ca..85477864 100644 --- a/tests/.config/.cppcheck_suppression_list +++ b/tests/.config/.cppcheck_suppression_list @@ -41,7 +41,7 @@ syntaxError:../library/math/prime_sieve/mobius.hpp:6 syntaxError:../library/trees/lca_rmq/iterate_subtree.hpp:6 knownConditionTrueFalse:../library/strings/suffix_array/suffix_array.hpp:62 knownConditionTrueFalse:../library/strings/suffix_array/suffix_array_short.hpp:34 -knownConditionTrueFalse:../library/dsu/kruskal_tree.hpp:15 +knownConditionTrueFalse:../library/dsu/kruskal_tree.hpp:13 knownConditionTrueFalse:../library/dsu/dsu.hpp:11 constVariable:../kactl/content/numerical/NumberTheoreticTransform.h:30 constVariable:../kactl/content/graph/CompressTree.h:20 diff --git a/tests/library_checker_aizu_tests/data_structures/dsu.test.cpp b/tests/library_checker_aizu_tests/dsu/dsu.test.cpp similarity index 100% rename from tests/library_checker_aizu_tests/data_structures/dsu.test.cpp rename to tests/library_checker_aizu_tests/dsu/dsu.test.cpp diff --git a/tests/library_checker_aizu_tests/data_structures/dsu_bipartite.test.cpp b/tests/library_checker_aizu_tests/dsu/dsu_bipartite.test.cpp similarity index 92% rename from tests/library_checker_aizu_tests/data_structures/dsu_bipartite.test.cpp rename to tests/library_checker_aizu_tests/dsu/dsu_bipartite.test.cpp index 77b46f82..9dd79a4d 100644 --- a/tests/library_checker_aizu_tests/data_structures/dsu_bipartite.test.cpp +++ b/tests/library_checker_aizu_tests/dsu/dsu_bipartite.test.cpp @@ -42,8 +42,8 @@ int main() { dsu.join(u, v); adj[u].push_back(v); adj[v].push_back(u); - } else cout << dsu.same_set(u, v) << '\n'; - if (rnd(0, 20'000) == 0) check(); + } else cout << (dsu.f(u) == dsu.f(v)) << '\n'; + if (i <= 20 || rnd(0, 20'000) == 0) check(); } check(); return 0; diff --git a/tests/library_checker_aizu_tests/data_structures/kruskal_tree_aizu.test.cpp b/tests/library_checker_aizu_tests/dsu/kruskal_tree_aizu.test.cpp similarity index 100% rename from tests/library_checker_aizu_tests/data_structures/kruskal_tree_aizu.test.cpp rename to tests/library_checker_aizu_tests/dsu/kruskal_tree_aizu.test.cpp diff --git a/tests/library_checker_aizu_tests/data_structures/line_tree_aizu.test.cpp b/tests/library_checker_aizu_tests/dsu/line_tree_aizu.test.cpp similarity index 97% rename from tests/library_checker_aizu_tests/data_structures/line_tree_aizu.test.cpp rename to tests/library_checker_aizu_tests/dsu/line_tree_aizu.test.cpp index 9efbd9ed..e7dd516d 100644 --- a/tests/library_checker_aizu_tests/data_structures/line_tree_aizu.test.cpp +++ b/tests/library_checker_aizu_tests/dsu/line_tree_aizu.test.cpp @@ -28,7 +28,7 @@ int main() { int mst_sum = 0; vector edge_weights; vector to_time(n); - for (int v = lt.find(0), timer = 1; + for (int v = lt.f(0), timer = 1; lt.edge[v] != pii{-1, -1}; v = lt.edge[v].first, timer++) { edge_weights.push_back(w_eds[lt.edge[v].second][0]); diff --git a/tests/library_checker_aizu_tests/data_structures/line_tree_lib_checker.test.cpp b/tests/library_checker_aizu_tests/dsu/line_tree_lib_checker.test.cpp similarity index 95% rename from tests/library_checker_aizu_tests/data_structures/line_tree_lib_checker.test.cpp rename to tests/library_checker_aizu_tests/dsu/line_tree_lib_checker.test.cpp index 65b3c5e4..535f686b 100644 --- a/tests/library_checker_aizu_tests/data_structures/line_tree_lib_checker.test.cpp +++ b/tests/library_checker_aizu_tests/dsu/line_tree_lib_checker.test.cpp @@ -22,7 +22,7 @@ int main() { assert(lt.size(0) == n); int64_t cost = 0; vector ids; - for (int v = lt.find(0); lt.edge[v].first != -1; + for (int v = lt.f(0); lt.edge[v].first != -1; v = lt.edge[v].first) { ids.push_back(w_eds[lt.edge[v].second][0]); cost += weights[w_eds[lt.edge[v].second][0]]; diff --git a/tests/library_checker_aizu_tests/data_structures/range_parallel_dsu.test.cpp b/tests/library_checker_aizu_tests/dsu/range_parallel_dsu.test.cpp similarity index 71% rename from tests/library_checker_aizu_tests/data_structures/range_parallel_dsu.test.cpp rename to tests/library_checker_aizu_tests/dsu/range_parallel_dsu.test.cpp index b4b46597..deb749d1 100644 --- a/tests/library_checker_aizu_tests/data_structures/range_parallel_dsu.test.cpp +++ b/tests/library_checker_aizu_tests/dsu/range_parallel_dsu.test.cpp @@ -9,26 +9,31 @@ int main() { cin.tie(0)->sync_with_stdio(0); int n, q; cin >> n >> q; - rp_dsu dsu(n); + rp_dsu r_dsu(n); vi y(n); for (int i = 0; i < n; i++) cin >> y[i]; vi x = y; int ans = 0; + DSU dsu(n); auto f = [&](int u, int v) { - ans = (ans + 1LL * x[u] * x[v]) % mod; - x[u] = (x[u] + x[v]) % mod; + u = dsu.f(u); + v = dsu.f(v); + assert(dsu.join(u, v)); + int root = dsu.f(u); + int other = root ^ u ^ v; + ans = (ans + 1LL * x[root] * x[other]) % mod; + x[root] = (x[root] + x[other]) % mod; }; - vector> queries; - queries.reserve(q); + vector> joins(n + 1); for (int qq = 0; qq < q; qq++) { int k, a, b; cin >> k >> a >> b; - dsu.join(a, b, k, f); - queries.push_back({a, b, k}); + if (k) r_dsu.join(a, b, k, f); + joins[k].emplace_back(a, b); cout << ans << '\n'; if (qq == 0 || qq == 1 || qq == 10 || qq == 1000 || qq == 100'000 || qq == q - 1) { - auto uf = get_rp_dsu(queries, n); + auto uf = get_rp_dsu(joins, n); vi sums(n); int offline_ans = 0; for (int i = 0; i < n; i++) { diff --git a/tests/scripts/compile_commented_snippets.sh b/tests/scripts/compile_commented_snippets.sh index 2d9c54b3..3caa3df9 100755 --- a/tests/scripts/compile_commented_snippets.sh +++ b/tests/scripts/compile_commented_snippets.sh @@ -25,7 +25,7 @@ git submodule update echo "vi rhs;" echo "vector mat;" echo "vector> grid;" - echo "int n,m,k,tl,tr,l,r,l1,r1,l2,r2,s_l,s_r,root_l,root_r,source,sink,total_flow,bccid,u,v,lsz,rsz,cols,cap,num,x,y,i,j,i1,i2,j1,j2;" + echo "int n,m,k,tl,tr,l,r,l1,r1,l2,r2,s_l,s_r,root_l,root_r,source,sink,total_flow,bccid,u,v,lsz,rsz,cols,cap,num,x,y,i,j,i1,i2,j1,j2,len;" } >entire_library_without_main {