diff --git a/README.md b/README.md index 5741a94..d60d191 100644 --- a/README.md +++ b/README.md @@ -5,47 +5,51 @@ My implementations of various data structures and algorithms for competitive pro - [0-common](https://github.com/manoflearning/cp-reference-codes/tree/main/src/0-common) - [`common.hpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/0-common/common.hpp) - [1-ds](https://github.com/manoflearning/cp-reference-codes/tree/main/src/1-ds) - - [`erasable_pq.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/1-ds/erasable_pq.cpp) - - [`fenwick_tree.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/1-ds/fenwick_tree.cpp) - - [`li_chao_tree.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/1-ds/li_chao_tree.cpp) - - [`merge_sort_tree.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/1-ds/merge_sort_tree.cpp) - - [`pbds.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/1-ds/pbds.cpp) - - [`segment_tree.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/1-ds/segment_tree.cpp) - - [`union_find.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/1-ds/union_find.cpp) + - [`ds_dsu.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/1-ds/ds_dsu.cpp) + - [`ds_erasable_pq.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/1-ds/ds_erasable_pq.cpp) + - [`ds_fenwick.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/1-ds/ds_fenwick.cpp) + - [`ds_li_chao.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/1-ds/ds_li_chao.cpp) + - [`ds_merge_seg.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/1-ds/ds_merge_seg.cpp) + - [`ds_pbds.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/1-ds/ds_pbds.cpp) + - [`ds_segtree.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/1-ds/ds_segtree.cpp) + - [`ds_segtree_pst.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/1-ds/ds_segtree_pst.cpp) - [2-graph](https://github.com/manoflearning/cp-reference-codes/tree/main/src/2-graph) - - [`bcc.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/2-graph/bcc.cpp) - - [`euler_circuit.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/2-graph/euler_circuit.cpp) - - [`kth_shortest_path.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/2-graph/kth_shortest_path.cpp) - - [`offline_dynamic_connectivity.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/2-graph/offline_dynamic_connectivity.cpp) - - [`scc_2_sat.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/2-graph/scc_2_sat.cpp) - - [`shortest_path.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/2-graph/shortest_path.cpp) + - [`cc_bcc.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/2-graph/cc_bcc.cpp) + - [`cc_scc.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/2-graph/cc_scc.cpp) + - [`dyncon_off.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/2-graph/dyncon_off.cpp) + - [`euler_circ.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/2-graph/euler_circ.cpp) + - [`sat_2sat.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/2-graph/sat_2sat.cpp) + - [`sp_basic.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/2-graph/sp_basic.cpp) + - [`sp_kth.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/2-graph/sp_kth.cpp) - [3-tree](https://github.com/manoflearning/cp-reference-codes/tree/main/src/3-tree) - - [`centroid_decomp.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/3-tree/centroid_decomp.cpp) - - [`hld.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/3-tree/hld.cpp) - - [`lca_sparse_table.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/3-tree/lca_sparse_table.cpp) - - [`tree_composition.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/3-tree/tree_composition.cpp) - - [`tree_exchange_argument.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/3-tree/tree_exchange_argument.cpp) + - [`lca_sparse.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/3-tree/lca_sparse.cpp) + - [`tree_centroid.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/3-tree/tree_centroid.cpp) + - [`tree_hld.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/3-tree/tree_hld.cpp) + - [`tree_vtree.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/3-tree/tree_vtree.cpp) + - [`tree_xchg.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/3-tree/tree_xchg.cpp) - [4-optimizations](https://github.com/manoflearning/cp-reference-codes/tree/main/src/4-optimizations) - - [`flow.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/4-optimizations/flow.cpp) - - [`hungarian.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/4-optimizations/hungarian.cpp) + - [`assign_hung.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/4-optimizations/assign_hung.cpp) + - [`flow_dinic.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/4-optimizations/flow_dinic.cpp) + - [`flow_hk.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/4-optimizations/flow_hk.cpp) + - [`flow_mcmf.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/4-optimizations/flow_mcmf.cpp) - [5-string](https://github.com/manoflearning/cp-reference-codes/tree/main/src/5-string) - - [`aho_corasick.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/5-string/aho_corasick.cpp) - - [`kmp_algorithm.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/5-string/kmp_algorithm.cpp) - - [`manachers_algorithm.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/5-string/manachers_algorithm.cpp) - - [`rabin_karp_algorithm.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/5-string/rabin_karp_algorithm.cpp) - - [`suffix_array.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/5-string/suffix_array.cpp) - - [`trie.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/5-string/trie.cpp) - - [`z_algorithm.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/5-string/z_algorithm.cpp) + - [`hash_rk.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/5-string/hash_rk.cpp) + - [`match_ac.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/5-string/match_ac.cpp) + - [`match_kmp.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/5-string/match_kmp.cpp) + - [`match_z.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/5-string/match_z.cpp) + - [`pal_manacher.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/5-string/pal_manacher.cpp) + - [`str_trie.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/5-string/str_trie.cpp) + - [`suffix_sa.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/5-string/suffix_sa.cpp) - [6-geometry](https://github.com/manoflearning/cp-reference-codes/tree/main/src/6-geometry) - - [`bulldozer_trick.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/6-geometry/bulldozer_trick.cpp) - - [`convex_hull.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/6-geometry/convex_hull.cpp) + - [`geom_ang_sort.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/6-geometry/geom_ang_sort.cpp) - [`geom_base.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/6-geometry/geom_base.cpp) - - [`half_plane_intersection.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/6-geometry/half_plane_intersection.cpp) - - [`minimum_enclosing_circle.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/6-geometry/minimum_enclosing_circle.cpp) - - [`ray_casting.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/6-geometry/ray_casting.cpp) - - [`rotating_callipers.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/6-geometry/rotating_callipers.cpp) - - [`segment_intersection.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/6-geometry/segment_intersection.cpp) - - [`sort_by_angular.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/6-geometry/sort_by_angular.cpp) + - [`geom_bulldozer.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/6-geometry/geom_bulldozer.cpp) + - [`geom_calipers.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/6-geometry/geom_calipers.cpp) + - [`geom_hpi.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/6-geometry/geom_hpi.cpp) + - [`geom_hull.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/6-geometry/geom_hull.cpp) + - [`geom_mec.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/6-geometry/geom_mec.cpp) + - [`geom_raycast.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/6-geometry/geom_raycast.cpp) + - [`geom_seg_inter.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/6-geometry/geom_seg_inter.cpp) - [7-math](https://github.com/manoflearning/cp-reference-codes/tree/main/src/7-math) - [`comb_binom.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/7-math/comb_binom.cpp) - [`comb_cat_der.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/7-math/comb_cat_der.cpp) @@ -61,13 +65,13 @@ My implementations of various data structures and algorithms for competitive pro - [`num.hpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/7-math/num.hpp) - [8-misc](https://github.com/manoflearning/cp-reference-codes/tree/main/src/8-misc) - [`dp_opt.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/8-misc/dp_opt.cpp) - - [`fraction_data_type.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/8-misc/fraction_data_type.cpp) - - [`kitamasa.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/8-misc/kitamasa.cpp) - - [`lis_in_o_nlogn.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/8-misc/lis_in_o_nlogn.cpp) - - [`random.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/8-misc/random.cpp) + - [`diff_cons.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/8-misc/diff_cons.cpp) + - [`num_frac.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/8-misc/num_frac.cpp) + - [`rec_kitamasa.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/8-misc/rec_kitamasa.cpp) + - [`rnd.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/8-misc/rnd.cpp) - [`rotation_matrix_manhattan_distance_chebyshev_distance.txt`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/8-misc/rotation_matrix_manhattan_distance_chebyshev_distance.txt) + - [`search_ternary.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/8-misc/search_ternary.cpp) + - [`seq_lis.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/8-misc/seq_lis.cpp) - [`simd.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/8-misc/simd.cpp) - - [`sqrt_decomposition_mos_algorithm.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/8-misc/sqrt_decomposition_mos_algorithm.cpp) + - [`sqrt_mo.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/8-misc/sqrt_mo.cpp) - [`stress_test.py`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/8-misc/stress_test.py) - - [`system_of_difference_constraints.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/8-misc/system_of_difference_constraints.cpp) - - [`ternary_search.cpp`](https://github.com/manoflearning/cp-reference-codes/blob/main/src/8-misc/ternary_search.cpp) diff --git a/src/1-ds/union_find.cpp b/src/1-ds/ds_dsu.cpp similarity index 100% rename from src/1-ds/union_find.cpp rename to src/1-ds/ds_dsu.cpp diff --git a/src/1-ds/erasable_pq.cpp b/src/1-ds/ds_erasable_pq.cpp similarity index 100% rename from src/1-ds/erasable_pq.cpp rename to src/1-ds/ds_erasable_pq.cpp diff --git a/src/1-ds/fenwick_tree.cpp b/src/1-ds/ds_fenwick.cpp similarity index 100% rename from src/1-ds/fenwick_tree.cpp rename to src/1-ds/ds_fenwick.cpp diff --git a/src/1-ds/li_chao_tree.cpp b/src/1-ds/ds_li_chao.cpp similarity index 100% rename from src/1-ds/li_chao_tree.cpp rename to src/1-ds/ds_li_chao.cpp diff --git a/src/1-ds/merge_sort_tree.cpp b/src/1-ds/ds_merge_seg.cpp similarity index 100% rename from src/1-ds/merge_sort_tree.cpp rename to src/1-ds/ds_merge_seg.cpp diff --git a/src/1-ds/pbds.cpp b/src/1-ds/ds_pbds.cpp similarity index 100% rename from src/1-ds/pbds.cpp rename to src/1-ds/ds_pbds.cpp diff --git a/src/1-ds/segment_tree.cpp b/src/1-ds/ds_segtree.cpp similarity index 78% rename from src/1-ds/segment_tree.cpp rename to src/1-ds/ds_segtree.cpp index 07b1a64..2b0abfc 100644 --- a/src/1-ds/segment_tree.cpp +++ b/src/1-ds/ds_segtree.cpp @@ -138,94 +138,6 @@ struct seg_tree_lz { } }; -// what: keep all versions of point updates with range sum queries, plus kth by prefix sum. -// time: build O(n), update/query O(log n); memory: O(n log n) -// constraint: 1-indexed [1, n]; a[0] unused; kth needs all values >= 0. -// usage: seg_pst st; st.build(n, a); st.set(p, v); st.query(l, r, ver); st.kth(k, ver); -struct seg_pst { - struct node { - int l, r; - ll val; - }; - int n; - vector t; - vector root; - - void newnd() { t.push_back({-1, -1, 0}); } - void build(int n_, const vector &a) { - // goal: build initial version. - n = n_; - t.clear(); - root.clear(); - newnd(); - root.push_back(0); - build(1, n, root[0], a); - } - void build(int l, int r, int v, const vector &a) { - // goal: build node v for range [l, r]. - if (l == r) { - t[v].val = a[l]; - return; - } - newnd(); - t[v].l = sz(t) - 1; - newnd(); - t[v].r = sz(t) - 1; - int mid = (l + r) >> 1; - build(l, mid, t[v].l, a); - build(mid + 1, r, t[v].r, a); - t[v].val = t[t[v].l].val + t[t[v].r].val; - } - void set(int p, ll val) { - // goal: create new version with a[p] = val. - newnd(); - root.push_back(sz(t) - 1); - set(p, val, 1, n, root[sz(root) - 2], root.back()); - } - void set(int p, ll val, int l, int r, int v1, int v2) { - // goal: update along path while sharing unchanged nodes. - if (p < l || r < p) { - t[v2] = t[v1]; - return; - } - if (l == r) { - t[v2].val = val; - return; - } - int mid = (l + r) >> 1; - if (p <= mid) { - t[v2].r = t[v1].r; - newnd(); - t[v2].l = sz(t) - 1; - set(p, val, l, mid, t[v1].l, t[v2].l); - } else { - t[v2].l = t[v1].l; - newnd(); - t[v2].r = sz(t) - 1; - set(p, val, mid + 1, r, t[v1].r, t[v2].r); - } - t[v2].val = t[t[v2].l].val + t[t[v2].r].val; - } - ll query(int l, int r, int v, int nl, int nr) const { - // result: sum on [l, r] in a specific version. - if (r < nl || nr < l) return 0; - if (l <= nl && nr <= r) return t[v].val; - int mid = (nl + nr) >> 1; - return query(l, r, t[v].l, nl, mid) + query(l, r, t[v].r, mid + 1, nr); - } - ll query(int l, int r, int ver) const { return query(l, r, root[ver], 1, n); } - int kth(ll k, int v, int nl, int nr) const { - // result: smallest idx with prefix sum >= k (in this subtree). - assert(k > 0 && t[v].val >= k); - if (nl == nr) return nl; - int mid = (nl + nr) >> 1; - ll lv = t[t[v].l].val; - if (k <= lv) return kth(k, t[v].l, nl, mid); - return kth(k - lv, t[v].r, mid + 1, nr); - } - int kth(ll k, int ver) const { return kth(k, root[ver], 1, n); } -}; - // what: sparse segment tree for large coordinate range (point add, range sum). // time: update/query O(log R); memory: O(k log R) // constraint: range [MAXL, MAXR], missing child => 0. diff --git a/src/1-ds/ds_segtree_pst.cpp b/src/1-ds/ds_segtree_pst.cpp new file mode 100644 index 0000000..ed5621b --- /dev/null +++ b/src/1-ds/ds_segtree_pst.cpp @@ -0,0 +1,89 @@ +#include "../0-common/common.hpp" + +// what: persistent segment tree for point set with range sum queries, plus kth by prefix sum. +// time: build O(n), update/query O(log n); memory: O(n log n) +// constraint: 1-indexed [1, n]; a[0] unused; kth needs all values >= 0. +// usage: seg_pst st; st.build(n, a); st.set(p, v); st.query(l, r, ver); st.kth(k, ver); +struct seg_pst { + struct node { + int l, r; + ll val; + }; + int n; + vector t; + vector root; + + void newnd() { t.push_back({-1, -1, 0}); } + void build(int n_, const vector &a) { + // goal: build initial version. + n = n_; + t.clear(); + root.clear(); + newnd(); + root.push_back(0); + build(1, n, root[0], a); + } + void build(int l, int r, int v, const vector &a) { + // goal: build node v for range [l, r]. + if (l == r) { + t[v].val = a[l]; + return; + } + newnd(); + t[v].l = sz(t) - 1; + newnd(); + t[v].r = sz(t) - 1; + int mid = (l + r) >> 1; + build(l, mid, t[v].l, a); + build(mid + 1, r, t[v].r, a); + t[v].val = t[t[v].l].val + t[t[v].r].val; + } + void set(int p, ll val) { + // goal: create new version with a[p] = val. + newnd(); + root.push_back(sz(t) - 1); + set(p, val, 1, n, root[sz(root) - 2], root.back()); + } + void set(int p, ll val, int l, int r, int v1, int v2) { + // goal: update along path while sharing unchanged nodes. + if (p < l || r < p) { + t[v2] = t[v1]; + return; + } + if (l == r) { + t[v2].val = val; + return; + } + int mid = (l + r) >> 1; + if (p <= mid) { + t[v2].r = t[v1].r; + newnd(); + t[v2].l = sz(t) - 1; + set(p, val, l, mid, t[v1].l, t[v2].l); + } else { + t[v2].l = t[v1].l; + newnd(); + t[v2].r = sz(t) - 1; + set(p, val, mid + 1, r, t[v1].r, t[v2].r); + } + t[v2].val = t[t[v2].l].val + t[t[v2].r].val; + } + ll query(int l, int r, int v, int nl, int nr) const { + // result: sum on [l, r] in a specific version. + if (r < nl || nr < l) return 0; + if (l <= nl && nr <= r) return t[v].val; + int mid = (nl + nr) >> 1; + return query(l, r, t[v].l, nl, mid) + query(l, r, t[v].r, mid + 1, nr); + } + ll query(int l, int r, int ver) const { return query(l, r, root[ver], 1, n); } + int kth(ll k, int v, int nl, int nr) const { + // result: smallest idx with prefix sum >= k (in this subtree). + assert(k > 0 && t[v].val >= k); + if (nl == nr) return nl; + int mid = (nl + nr) >> 1; + ll lv = t[t[v].l].val; + if (k <= lv) return kth(k, t[v].l, nl, mid); + return kth(k - lv, t[v].r, mid + 1, nr); + } + int kth(ll k, int ver) const { return kth(k, root[ver], 1, n); } +}; diff --git a/src/2-graph/bcc.cpp b/src/2-graph/cc_bcc.cpp similarity index 100% rename from src/2-graph/bcc.cpp rename to src/2-graph/cc_bcc.cpp diff --git a/src/2-graph/scc_2_sat.cpp b/src/2-graph/cc_scc.cpp similarity index 60% rename from src/2-graph/scc_2_sat.cpp rename to src/2-graph/cc_scc.cpp index c46e977..492e584 100644 --- a/src/2-graph/scc_2_sat.cpp +++ b/src/2-graph/cc_scc.cpp @@ -98,66 +98,3 @@ struct scc_tarjan { return sz(sccs); } }; - -// what: solve 2-SAT by SCC condensation (Tarjan-based). -// time: O(n+m); memory: O(n+m) -// constraint: vars are 1..n; literal x<0 means not x; recursion depth O(n). -// usage: two_sat s; s.init(n); s.add(a,b); bool ok=s.run(); // s.val -struct two_sat { - int n, tim, cid; - vector> g; - vector dfn, low, comp, st, ins, val; - - void init(int n_) { - n = n_; - g.assign(2 * n, {}); - dfn.assign(2 * n, -1); - low.assign(2 * n, 0); - comp.assign(2 * n, -1); - ins.assign(2 * n, 0); - st.clear(); - val.assign(n + 1, 0); - tim = 0; - cid = 0; - } - int id(int x) { - // goal: x in [-n, n]\{0} -> node id. - return x > 0 ? 2 * (x - 1) : 2 * (-x - 1) + 1; - } - void add(int a, int b) { - // goal: (a v b) == (!a -> b) & (!b -> a) - g[id(-a)].push_back(id(b)); - g[id(-b)].push_back(id(a)); - } - void dfs(int v) { - dfn[v] = low[v] = ++tim; - st.push_back(v); - ins[v] = 1; - for (int to : g[v]) { - if (dfn[to] == -1) { - dfs(to); - low[v] = min(low[v], low[to]); - } else if (ins[to]) { - low[v] = min(low[v], dfn[to]); - } - } - if (low[v] != dfn[v]) return; - while (1) { - int x = st.back(); - st.pop_back(); - ins[x] = 0; - comp[x] = cid; - if (x == v) break; - } - cid++; - } - bool run() { - for (int i = 0; i < 2 * n; i++) - if (dfn[i] == -1) dfs(i); - for (int i = 0; i < n; i++) { - if (comp[2 * i] == comp[2 * i + 1]) return 0; - val[i + 1] = comp[2 * i] < comp[2 * i + 1]; - } - return 1; - } -}; diff --git a/src/2-graph/offline_dynamic_connectivity.cpp b/src/2-graph/dyncon_off.cpp similarity index 100% rename from src/2-graph/offline_dynamic_connectivity.cpp rename to src/2-graph/dyncon_off.cpp diff --git a/src/2-graph/euler_circuit.cpp b/src/2-graph/euler_circ.cpp similarity index 100% rename from src/2-graph/euler_circuit.cpp rename to src/2-graph/euler_circ.cpp diff --git a/src/2-graph/sat_2sat.cpp b/src/2-graph/sat_2sat.cpp new file mode 100644 index 0000000..98a2108 --- /dev/null +++ b/src/2-graph/sat_2sat.cpp @@ -0,0 +1,64 @@ +#include "../0-common/common.hpp" + +// what: solve 2-SAT by SCC condensation (Tarjan-based). +// time: O(n+m); memory: O(n+m) +// constraint: vars are 1..n; literal x<0 means not x; recursion depth O(n). +// usage: two_sat s; s.init(n); s.add(a,b); bool ok=s.run(); // s.val +struct two_sat { + int n, tim, cid; + vector> g; + vector dfn, low, comp, st, ins, val; + + void init(int n_) { + n = n_; + g.assign(2 * n, {}); + dfn.assign(2 * n, -1); + low.assign(2 * n, 0); + comp.assign(2 * n, -1); + ins.assign(2 * n, 0); + st.clear(); + val.assign(n + 1, 0); + tim = 0; + cid = 0; + } + int id(int x) { + // goal: x in [-n, n]\{0} -> node id. + return x > 0 ? 2 * (x - 1) : 2 * (-x - 1) + 1; + } + void add(int a, int b) { + // goal: (a v b) == (!a -> b) & (!b -> a) + g[id(-a)].push_back(id(b)); + g[id(-b)].push_back(id(a)); + } + void dfs(int v) { + dfn[v] = low[v] = ++tim; + st.push_back(v); + ins[v] = 1; + for (int to : g[v]) { + if (dfn[to] == -1) { + dfs(to); + low[v] = min(low[v], low[to]); + } else if (ins[to]) { + low[v] = min(low[v], dfn[to]); + } + } + if (low[v] != dfn[v]) return; + while (1) { + int x = st.back(); + st.pop_back(); + ins[x] = 0; + comp[x] = cid; + if (x == v) break; + } + cid++; + } + bool run() { + for (int i = 0; i < 2 * n; i++) + if (dfn[i] == -1) dfs(i); + for (int i = 0; i < n; i++) { + if (comp[2 * i] == comp[2 * i + 1]) return 0; + val[i + 1] = comp[2 * i] < comp[2 * i + 1]; + } + return 1; + } +}; diff --git a/src/2-graph/shortest_path.cpp b/src/2-graph/sp_basic.cpp similarity index 100% rename from src/2-graph/shortest_path.cpp rename to src/2-graph/sp_basic.cpp diff --git a/src/2-graph/kth_shortest_path.cpp b/src/2-graph/sp_kth.cpp similarity index 100% rename from src/2-graph/kth_shortest_path.cpp rename to src/2-graph/sp_kth.cpp diff --git a/src/3-tree/lca_sparse_table.cpp b/src/3-tree/lca_sparse.cpp similarity index 100% rename from src/3-tree/lca_sparse_table.cpp rename to src/3-tree/lca_sparse.cpp diff --git a/src/3-tree/centroid_decomp.cpp b/src/3-tree/tree_centroid.cpp similarity index 100% rename from src/3-tree/centroid_decomp.cpp rename to src/3-tree/tree_centroid.cpp diff --git a/src/3-tree/hld.cpp b/src/3-tree/tree_hld.cpp similarity index 98% rename from src/3-tree/hld.cpp rename to src/3-tree/tree_hld.cpp index 035d48a..fcd8f9a 100644 --- a/src/3-tree/hld.cpp +++ b/src/3-tree/tree_hld.cpp @@ -1,4 +1,4 @@ -#include "../1-ds/segment_tree.cpp" +#include "../1-ds/ds_segtree.cpp" // what: heavy-light decomposition for path sum on tree (node values). // time: build O(n), update/query O(log^2 n); memory: O(n) diff --git a/src/3-tree/tree_composition.cpp b/src/3-tree/tree_vtree.cpp similarity index 100% rename from src/3-tree/tree_composition.cpp rename to src/3-tree/tree_vtree.cpp diff --git a/src/3-tree/tree_exchange_argument.cpp b/src/3-tree/tree_xchg.cpp similarity index 100% rename from src/3-tree/tree_exchange_argument.cpp rename to src/3-tree/tree_xchg.cpp diff --git a/src/4-optimizations/hungarian.cpp b/src/4-optimizations/assign_hung.cpp similarity index 100% rename from src/4-optimizations/hungarian.cpp rename to src/4-optimizations/assign_hung.cpp diff --git a/src/4-optimizations/flow.cpp b/src/4-optimizations/flow.cpp deleted file mode 100644 index c0ce5ac..0000000 --- a/src/4-optimizations/flow.cpp +++ /dev/null @@ -1,465 +0,0 @@ -#include "../0-common/common.hpp" - -// what: collection of flow solvers (max flow, min-cost flow, matching, bounds). -// time: see each struct; memory: O(E) -// constraint: 0-based; cap >= 0; mcmf assumes no negative cycle reachable from s. -// usage: dinic mf(n); mf.add_edge(u,v,c); ll f=mf.max_flow(s,t); - -// dinic max flow (0-based). -// what: compute maximum flow in a directed graph (Dinic). -// time: O(E V^2) worst; memory: O(E) -// constraint: cap >= 0 -// usage: dinic mf(n); mf.add_edge(u, v, cap); ll f = mf.max_flow(s, t); -struct dinic { - static constexpr ll INF = (1LL << 62); - - struct edge { - int to, rev; - ll cap; - }; - struct edge_ref { - int u, idx; - }; - - int n; - vector> g; - vector level, work; - - dinic(int n = 0) { init(n); } - void init(int n_) { - n = n_; - g.assign(n, {}); - } - edge_ref add_edge(int u, int v, ll cap) { - // goal: add forward + reverse edge - edge a{v, sz(g[v]), cap}; - edge b{u, sz(g[u]), 0}; - g[u].push_back(a); - g[v].push_back(b); - return {u, sz(g[u]) - 1}; - } - ll edge_flow(edge_ref e) const { - // goal: current flow on original edge - const edge &ed = g[e.u][e.idx]; - return g[ed.to][ed.rev].cap; - } - void clear_edge(edge_ref e) { - // goal: remove edge from residual graph - edge &ed = g[e.u][e.idx]; - edge &rev = g[ed.to][ed.rev]; - ed.cap = 0; - rev.cap = 0; - } - - bool bfs(int s, int t) { - // goal: build level graph - level.assign(n, -1); - queue q; - level[s] = 0; - q.push(s); - while (!q.empty()) { - int v = q.front(); - q.pop(); - for (const auto &e : g[v]) { - if (e.cap == 0 || level[e.to] != -1) continue; - level[e.to] = level[v] + 1; - q.push(e.to); - } - } - return level[t] != -1; - } - ll dfs(int v, int t, ll f) { - if (v == t || f == 0) return f; - for (int &i = work[v]; i < sz(g[v]); i++) { - edge &e = g[v][i]; - if (e.cap == 0 || level[e.to] != level[v] + 1) continue; - // invariant: level strictly increases along augmenting path - ll pushed = dfs(e.to, t, min(f, e.cap)); - if (pushed == 0) continue; - e.cap -= pushed; - g[e.to][e.rev].cap += pushed; - return pushed; - } - return 0; - } - ll max_flow(int s, int t, ll limit = INF) { - if (s == t) return 0; // edge: no flow needed - ll flow = 0; - while (flow < limit && bfs(s, t)) { - work.assign(n, 0); - while (flow < limit) { - ll pushed = dfs(s, t, limit - flow); - if (pushed == 0) break; - flow += pushed; - } - } - return flow; - } -}; - -// hk bipartite matching (0-based). -// what: compute maximum bipartite matching (Hopcroft-Karp). -// time: O(E sqrt V); memory: O(E) -// constraint: left [0..n_l-1], right [0..n_r-1] -// usage: hk bm(n_l, n_r); bm.add_edge(l, r); int m = bm.max_matching(); -struct hk { - int n_l, n_r; - vector> g; - vector dist, match_l, match_r; - - hk(int n_l_ = 0, int n_r_ = 0) { init(n_l_, n_r_); } - void init(int n_l_, int n_r_) { - n_l = n_l_; - n_r = n_r_; - g.assign(n_l, {}); - dist.assign(n_l, 0); - match_l.assign(n_l, -1); - match_r.assign(n_r, -1); - } - void add_edge(int l, int r) { - // goal: add edge from left to right - g[l].push_back(r); - } - - bool bfs() { - // goal: build layers for shortest augmenting paths - queue q; - fill(all(dist), -1); - for (int i = 0; i < n_l; i++) { - if (match_l[i] == -1) { - dist[i] = 0; - q.push(i); - } - } - bool found = false; - while (!q.empty()) { - int v = q.front(); - q.pop(); - for (int r : g[v]) { - int u = match_r[r]; - if (u == -1) { - found = true; - } else if (dist[u] == -1) { - dist[u] = dist[v] + 1; - q.push(u); - } - } - } - return found; - } - bool dfs(int v) { - for (int r : g[v]) { - int u = match_r[r]; - if (u == -1 || (dist[u] == dist[v] + 1 && dfs(u))) { - match_l[v] = r; - match_r[r] = v; - return true; - } - } - dist[v] = -1; - return false; - } - int max_matching() { - // goal: compute maximum matching size - fill(all(match_l), -1); - fill(all(match_r), -1); - int match = 0; - while (bfs()) { - for (int i = 0; i < n_l; i++) { - if (match_l[i] == -1 && dfs(i)) match++; - } - } - return match; - } -}; - -// mcmf min-cost max-flow (0-based). -// what: compute min-cost max-flow using potentials and Dijkstra. -// time: O(F E log V); memory: O(E) -// constraint: no negative cycle reachable from s -// usage: mcmf mf(n); mf.add_edge(u,v,c,co); pll r=mf.min_cost_mf(s,t); -// usage: init_pot = false if all costs >= 0 (faster) -struct mcmf { - static constexpr ll INF = (1LL << 62); - - struct edge { - int to, rev; - ll cap, cost; - }; - struct edge_ref { - int u, idx; - }; - - int n; - vector> g; - - mcmf(int n = 0) { init(n); } - void init(int n_) { - n = n_; - g.assign(n, {}); - } - edge_ref add_edge(int u, int v, ll cap, ll cost) { - // goal: add forward + reverse edge with costs - edge a{v, sz(g[v]), cap, cost}; - edge b{u, sz(g[u]), 0, -cost}; - g[u].push_back(a); - g[v].push_back(b); - return {u, sz(g[u]) - 1}; - } - ll edge_flow(edge_ref e) const { - // goal: current flow on original edge - const edge &ed = g[e.u][e.idx]; - return g[ed.to][ed.rev].cap; - } - void clear_edge(edge_ref e) { - // goal: remove edge from residual graph - edge &ed = g[e.u][e.idx]; - edge &rev = g[ed.to][ed.rev]; - ed.cap = 0; - rev.cap = 0; - } - - pll min_cost_mf(int s, int t, ll max_f = INF, bool init_pot = true) { - ll flow = 0, cost = 0; - vector pot(n, 0), dist(n); - vector pv(n), pe(n); - - if (init_pot) { - // goal: initial potentials for negative costs - vector d(n, INF); - vector in_q(n, 0); - queue q; - d[s] = 0; - q.push(s); - in_q[s] = 1; - while (!q.empty()) { - int v = q.front(); - q.pop(); - in_q[v] = 0; - for (const auto &e : g[v]) { - if (e.cap == 0) continue; - if (d[v] + e.cost < d[e.to]) { - d[e.to] = d[v] + e.cost; - if (!in_q[e.to]) { - in_q[e.to] = 1; - q.push(e.to); - } - } - } - } - for (int i = 0; i < n; i++) - if (d[i] < INF) pot[i] = d[i]; - } - while (flow < max_f) { - // goal: shortest path in reduced costs - fill(all(dist), INF); - dist[s] = 0; - priority_queue, vector>, greater>> pq; - pq.push({0, s}); - while (!pq.empty()) { - auto [d, v] = pq.top(); - pq.pop(); - if (d != dist[v]) continue; - for (int i = 0; i < sz(g[v]); i++) { - const auto &e = g[v][i]; - if (e.cap == 0) continue; - ll nd = d + e.cost + pot[v] - pot[e.to]; - if (nd < dist[e.to]) { - dist[e.to] = nd; - pv[e.to] = v; - pe[e.to] = i; - pq.push({nd, e.to}); - } - } - } - if (dist[t] == INF) break; // edge: no more augmenting paths - for (int i = 0; i < n; i++) - if (dist[i] < INF) pot[i] += dist[i]; - - ll add = max_f - flow; - for (int v = t; v != s; v = pv[v]) { - const auto &e = g[pv[v]][pe[v]]; - add = min(add, e.cap); - } - for (int v = t; v != s; v = pv[v]) { - auto &e = g[pv[v]][pe[v]]; - e.cap -= add; - g[v][e.rev].cap += add; - cost += add * e.cost; - } - flow += add; - } - - return {flow, cost}; - } -}; - -// lr_dinic (0-based). -// what: find feasible max flow with lower/upper bounds via transformation. -// time: dominated by dinic, memory: O(E) -// constraint: 0 <= lo <= hi, single-use (call init(n) to reuse) -// usage: lr_dinic f(n); int id = f.add_edge(u, v, lo, hi); auto [ok, v] = f.max_flow(s, t); -struct lr_dinic { - static constexpr ll INF = dinic::INF; - - struct edge_info { - dinic::edge_ref ref; - ll lo; - }; - - int n; - dinic mf; - vector demand; - vector edges; - - lr_dinic(int n = 0) { init(n); } - void init(int n_) { - n = n_; - mf.init(n + 2); - demand.assign(n, 0); - edges.clear(); - } - int add_edge(int u, int v, ll lo, ll hi) { - // goal: store lower bounds via node demands - demand[u] -= lo; - demand[v] += lo; - edges.push_back({mf.add_edge(u, v, hi - lo), lo}); - return sz(edges) - 1; - } - ll edge_flow(int id) const { - // goal: actual flow with lower bound restored - return edges[id].lo + mf.edge_flow(edges[id].ref); - } - ll add_demands(vector &aux) { - // goal: connect ss/tt for feasible circulation - ll total = 0; - int ss = n, tt = n + 1; - for (int i = 0; i < n; i++) { - if (demand[i] > 0) { - aux.push_back(mf.add_edge(ss, i, demand[i])); - total += demand[i]; - } else if (demand[i] < 0) { - aux.push_back(mf.add_edge(i, tt, -demand[i])); - } - } - return total; - } - - bool feasible() { - vector aux; - aux.reserve(n); - ll total = add_demands(aux); - int ss = n, tt = n + 1; - ll flow = mf.max_flow(ss, tt); - for (auto ref : aux) mf.clear_edge(ref); - return flow == total; - } - pair max_flow(int s, int t) { - if (s == t) return {feasible(), 0}; // edge: trivial s == t - vector aux; - aux.reserve(n + 1); - int ss = n, tt = n + 1; - auto ts = mf.add_edge(t, s, INF); - ll total = add_demands(aux); - ll flow = mf.max_flow(ss, tt); - if (flow != total) { - mf.clear_edge(ts); - for (auto ref : aux) mf.clear_edge(ref); - return {false, 0}; - } - ll base = mf.edge_flow(ts); - mf.clear_edge(ts); - for (auto ref : aux) mf.clear_edge(ref); - ll extra = mf.max_flow(s, t); - return {true, base + extra}; - } -}; - -// lr_mcmf (0-based). -// what: find min-cost flow with lower/upper bounds via transformation. -// time: dominated by mcmf, memory: O(E) -// constraint: 0 <= lo <= hi, no negative cycle, single-use (call init(n) to reuse) -// usage: lr_mcmf f(n); f.add_edge(u, v, lo, hi, cost); auto [ok, r] = f.max_flow(s, t); -struct lr_mcmf { - static constexpr ll INF = mcmf::INF; - - struct edge_info { - mcmf::edge_ref ref; - ll lo; - }; - - int n; - mcmf mf; - vector demand; - vector edges; - ll base_cost; - - lr_mcmf(int n = 0) { init(n); } - void init(int n_) { - n = n_; - mf.init(n + 2); - demand.assign(n, 0); - edges.clear(); - base_cost = 0; - } - int add_edge(int u, int v, ll lo, ll hi, ll cost) { - // goal: store lower bounds via node demands - demand[u] -= lo; - demand[v] += lo; - base_cost += lo * cost; - edges.push_back({mf.add_edge(u, v, hi - lo, cost), lo}); - return sz(edges) - 1; - } - ll edge_flow(int id) const { - // goal: actual flow with lower bound restored - return edges[id].lo + mf.edge_flow(edges[id].ref); - } - ll add_demands(vector &aux) { - // goal: connect ss/tt for feasible circulation - ll total = 0; - int ss = n, tt = n + 1; - for (int i = 0; i < n; i++) { - if (demand[i] > 0) { - aux.push_back(mf.add_edge(ss, i, demand[i], 0)); - total += demand[i]; - } else if (demand[i] < 0) { - aux.push_back(mf.add_edge(i, tt, -demand[i], 0)); - } - } - return total; - } - - pair feasible(bool init_pot = true) { - vector aux; - aux.reserve(n); - ll total = add_demands(aux); - int ss = n, tt = n + 1; - pll res = mf.min_cost_mf(ss, tt, total, init_pot); - for (auto ref : aux) mf.clear_edge(ref); - if (res.fr != total) return {false, 0}; - return {true, res.sc + base_cost}; - } - pair max_flow(int s, int t, bool init_pot = true) { - if (s == t) { - auto r = feasible(init_pot); - return {r.fr, {0, r.sc}}; - } - vector aux; - aux.reserve(n + 1); - int ss = n, tt = n + 1; - auto ts = mf.add_edge(t, s, INF, 0); - ll total = add_demands(aux); - pll res = mf.min_cost_mf(ss, tt, total, init_pot); - if (res.fr != total) { - mf.clear_edge(ts); - for (auto ref : aux) mf.clear_edge(ref); - return {false, {0, 0}}; - } - ll base = mf.edge_flow(ts); - ll cost = res.sc; - mf.clear_edge(ts); - for (auto ref : aux) mf.clear_edge(ref); - pll extra = mf.min_cost_mf(s, t, INF, init_pot); - return {true, {base + extra.fr, cost + extra.sc + base_cost}}; - } -}; diff --git a/src/4-optimizations/flow_dinic.cpp b/src/4-optimizations/flow_dinic.cpp new file mode 100644 index 0000000..93b59c6 --- /dev/null +++ b/src/4-optimizations/flow_dinic.cpp @@ -0,0 +1,172 @@ +#include "../0-common/common.hpp" + +// what: compute maximum flow in a directed graph (Dinic). +// time: O(E V^2) worst; memory: O(E) +// constraint: 0-based; cap >= 0. +// usage: dinic mf(n); mf.add_edge(u, v, cap); ll f = mf.max_flow(s, t); +struct dinic { + static constexpr ll INF = (1LL << 62); + + struct edge { + int to, rev; + ll cap; + }; + struct edge_ref { + int u, idx; + }; + + int n; + vector> g; + vector level, work; + + dinic(int n = 0) { init(n); } + void init(int n_) { + n = n_; + g.assign(n, {}); + } + edge_ref add_edge(int u, int v, ll cap) { + // goal: add forward + reverse edge + edge a{v, sz(g[v]), cap}; + edge b{u, sz(g[u]), 0}; + g[u].push_back(a); + g[v].push_back(b); + return {u, sz(g[u]) - 1}; + } + ll edge_flow(edge_ref e) const { + // goal: current flow on original edge + const edge &ed = g[e.u][e.idx]; + return g[ed.to][ed.rev].cap; + } + void clear_edge(edge_ref e) { + // goal: remove edge from residual graph + edge &ed = g[e.u][e.idx]; + edge &rev = g[ed.to][ed.rev]; + ed.cap = 0; + rev.cap = 0; + } + + bool bfs(int s, int t) { + // goal: build level graph + level.assign(n, -1); + queue q; + level[s] = 0; + q.push(s); + while (!q.empty()) { + int v = q.front(); + q.pop(); + for (const auto &e : g[v]) { + if (e.cap == 0 || level[e.to] != -1) continue; + level[e.to] = level[v] + 1; + q.push(e.to); + } + } + return level[t] != -1; + } + ll dfs(int v, int t, ll f) { + if (v == t || f == 0) return f; + for (int &i = work[v]; i < sz(g[v]); i++) { + edge &e = g[v][i]; + if (e.cap == 0 || level[e.to] != level[v] + 1) continue; + // invariant: level strictly increases along augmenting path + ll pushed = dfs(e.to, t, min(f, e.cap)); + if (pushed == 0) continue; + e.cap -= pushed; + g[e.to][e.rev].cap += pushed; + return pushed; + } + return 0; + } + ll max_flow(int s, int t, ll limit = INF) { + if (s == t) return 0; // edge: no flow needed + ll flow = 0; + while (flow < limit && bfs(s, t)) { + work.assign(n, 0); + while (flow < limit) { + ll pushed = dfs(s, t, limit - flow); + if (pushed == 0) break; + flow += pushed; + } + } + return flow; + } +}; + +// what: find feasible max flow with lower/upper bounds via transformation. +// time: dominated by dinic; memory: O(E) +// constraint: 0-based; 0 <= lo <= hi; single-use (call init(n) to reuse). +// usage: lr_dinic f(n); f.add_edge(u, v, lo, hi); auto [ok, fl] = f.max_flow(s, t); +struct lr_dinic { + static constexpr ll INF = dinic::INF; + + struct edge_info { + dinic::edge_ref ref; + ll lo; + }; + + int n; + dinic mf; + vector demand; + vector edges; + + lr_dinic(int n = 0) { init(n); } + void init(int n_) { + n = n_; + mf.init(n + 2); + demand.assign(n, 0); + edges.clear(); + } + int add_edge(int u, int v, ll lo, ll hi) { + // goal: store lower bounds via node demands + demand[u] -= lo; + demand[v] += lo; + edges.push_back({mf.add_edge(u, v, hi - lo), lo}); + return sz(edges) - 1; + } + ll edge_flow(int id) const { + // goal: actual flow with lower bound restored + return edges[id].lo + mf.edge_flow(edges[id].ref); + } + ll add_demands(vector &aux) { + // goal: connect ss/tt for feasible circulation + ll total = 0; + int ss = n, tt = n + 1; + for (int i = 0; i < n; i++) { + if (demand[i] > 0) { + aux.push_back(mf.add_edge(ss, i, demand[i])); + total += demand[i]; + } else if (demand[i] < 0) { + aux.push_back(mf.add_edge(i, tt, -demand[i])); + } + } + return total; + } + + bool feasible() { + vector aux; + aux.reserve(n); + ll total = add_demands(aux); + int ss = n, tt = n + 1; + ll flow = mf.max_flow(ss, tt); + for (auto ref : aux) mf.clear_edge(ref); + return flow == total; + } + pair max_flow(int s, int t) { + if (s == t) return {feasible(), 0}; // edge: trivial s == t + vector aux; + aux.reserve(n + 1); + int ss = n, tt = n + 1; + auto ts = mf.add_edge(t, s, INF); + ll total = add_demands(aux); + ll flow = mf.max_flow(ss, tt); + if (flow != total) { + mf.clear_edge(ts); + for (auto ref : aux) mf.clear_edge(ref); + return {false, 0}; + } + ll base = mf.edge_flow(ts); + mf.clear_edge(ts); + for (auto ref : aux) mf.clear_edge(ref); + ll extra = mf.max_flow(s, t); + return {true, base + extra}; + } +}; diff --git a/src/4-optimizations/flow_hk.cpp b/src/4-optimizations/flow_hk.cpp new file mode 100644 index 0000000..5490797 --- /dev/null +++ b/src/4-optimizations/flow_hk.cpp @@ -0,0 +1,76 @@ +#include "../0-common/common.hpp" + +// what: maximum bipartite matching (Hopcroft-Karp). +// time: O(E sqrt V); memory: O(E) +// constraint: 0-based; left [0..n_l-1], right [0..n_r-1] +// usage: hk bm(n_l, n_r); bm.add_edge(l, r); int m = bm.max_matching(); +struct hk { + int n_l, n_r; + vector> g; + vector dist, match_l, match_r; + + hk(int n_l_ = 0, int n_r_ = 0) { init(n_l_, n_r_); } + void init(int n_l_, int n_r_) { + n_l = n_l_; + n_r = n_r_; + g.assign(n_l, {}); + dist.assign(n_l, 0); + match_l.assign(n_l, -1); + match_r.assign(n_r, -1); + } + void add_edge(int l, int r) { + // goal: add edge from left to right + g[l].push_back(r); + } + + bool bfs() { + // goal: build layers for shortest augmenting paths + queue q; + fill(all(dist), -1); + for (int i = 0; i < n_l; i++) { + if (match_l[i] == -1) { + dist[i] = 0; + q.push(i); + } + } + bool found = false; + while (!q.empty()) { + int v = q.front(); + q.pop(); + for (int r : g[v]) { + int u = match_r[r]; + if (u == -1) { + found = true; + } else if (dist[u] == -1) { + dist[u] = dist[v] + 1; + q.push(u); + } + } + } + return found; + } + bool dfs(int v) { + for (int r : g[v]) { + int u = match_r[r]; + if (u == -1 || (dist[u] == dist[v] + 1 && dfs(u))) { + match_l[v] = r; + match_r[r] = v; + return true; + } + } + dist[v] = -1; + return false; + } + int max_matching() { + // goal: compute maximum matching size + fill(all(match_l), -1); + fill(all(match_r), -1); + int match = 0; + while (bfs()) { + for (int i = 0; i < n_l; i++) { + if (match_l[i] == -1 && dfs(i)) match++; + } + } + return match; + } +}; diff --git a/src/4-optimizations/flow_mcmf.cpp b/src/4-optimizations/flow_mcmf.cpp new file mode 100644 index 0000000..00647d3 --- /dev/null +++ b/src/4-optimizations/flow_mcmf.cpp @@ -0,0 +1,209 @@ +#include "../0-common/common.hpp" + +// what: compute min-cost max-flow using potentials and Dijkstra. +// time: O(F E log V); memory: O(E) +// constraint: 0-based; cap >= 0; no negative cycle reachable from s. +// usage: mcmf mf(n); mf.add_edge(u, v, cap, cost); pll r = mf.min_cost_mf(s, t); +// usage: init_pot = false if all costs >= 0 (faster) +struct mcmf { + static constexpr ll INF = (1LL << 62); + + struct edge { + int to, rev; + ll cap, cost; + }; + struct edge_ref { + int u, idx; + }; + + int n; + vector> g; + + mcmf(int n = 0) { init(n); } + void init(int n_) { + n = n_; + g.assign(n, {}); + } + edge_ref add_edge(int u, int v, ll cap, ll cost) { + // goal: add forward + reverse edge with costs + edge a{v, sz(g[v]), cap, cost}; + edge b{u, sz(g[u]), 0, -cost}; + g[u].push_back(a); + g[v].push_back(b); + return {u, sz(g[u]) - 1}; + } + ll edge_flow(edge_ref e) const { + // goal: current flow on original edge + const edge &ed = g[e.u][e.idx]; + return g[ed.to][ed.rev].cap; + } + void clear_edge(edge_ref e) { + // goal: remove edge from residual graph + edge &ed = g[e.u][e.idx]; + edge &rev = g[ed.to][ed.rev]; + ed.cap = 0; + rev.cap = 0; + } + + pll min_cost_mf(int s, int t, ll max_f = INF, bool init_pot = true) { + ll flow = 0, cost = 0; + vector pot(n, 0), dist(n); + vector pv(n), pe(n); + + if (init_pot) { + // goal: initial potentials for negative costs + vector d(n, INF); + vector in_q(n, 0); + queue q; + d[s] = 0; + q.push(s); + in_q[s] = 1; + while (!q.empty()) { + int v = q.front(); + q.pop(); + in_q[v] = 0; + for (const auto &e : g[v]) { + if (e.cap == 0) continue; + if (d[v] + e.cost < d[e.to]) { + d[e.to] = d[v] + e.cost; + if (!in_q[e.to]) { + in_q[e.to] = 1; + q.push(e.to); + } + } + } + } + for (int i = 0; i < n; i++) + if (d[i] < INF) pot[i] = d[i]; + } + while (flow < max_f) { + // goal: shortest path in reduced costs + fill(all(dist), INF); + dist[s] = 0; + priority_queue, vector>, greater>> pq; + pq.push({0, s}); + while (!pq.empty()) { + auto [d, v] = pq.top(); + pq.pop(); + if (d != dist[v]) continue; + for (int i = 0; i < sz(g[v]); i++) { + const auto &e = g[v][i]; + if (e.cap == 0) continue; + ll nd = d + e.cost + pot[v] - pot[e.to]; + if (nd < dist[e.to]) { + dist[e.to] = nd; + pv[e.to] = v; + pe[e.to] = i; + pq.push({nd, e.to}); + } + } + } + if (dist[t] == INF) break; // edge: no more augmenting paths + for (int i = 0; i < n; i++) + if (dist[i] < INF) pot[i] += dist[i]; + + ll add = max_f - flow; + for (int v = t; v != s; v = pv[v]) { + const auto &e = g[pv[v]][pe[v]]; + add = min(add, e.cap); + } + for (int v = t; v != s; v = pv[v]) { + auto &e = g[pv[v]][pe[v]]; + e.cap -= add; + g[v][e.rev].cap += add; + cost += add * e.cost; + } + flow += add; + } + + return {flow, cost}; + } +}; + +// what: find min-cost flow with lower/upper bounds via transformation. +// time: dominated by mcmf; memory: O(E) +// constraint: 0-based; 0 <= lo <= hi; no negative cycle; single-use (call init(n) to reuse). +// usage: lr_mcmf f(n); f.add_edge(u, v, lo, hi, cost); auto [ok, r] = f.max_flow(s, t); +struct lr_mcmf { + static constexpr ll INF = mcmf::INF; + + struct edge_info { + mcmf::edge_ref ref; + ll lo; + }; + + int n; + mcmf mf; + vector demand; + vector edges; + ll base_cost; + + lr_mcmf(int n = 0) { init(n); } + void init(int n_) { + n = n_; + mf.init(n + 2); + demand.assign(n, 0); + edges.clear(); + base_cost = 0; + } + int add_edge(int u, int v, ll lo, ll hi, ll cost) { + // goal: store lower bounds via node demands + demand[u] -= lo; + demand[v] += lo; + base_cost += lo * cost; + edges.push_back({mf.add_edge(u, v, hi - lo, cost), lo}); + return sz(edges) - 1; + } + ll edge_flow(int id) const { + // goal: actual flow with lower bound restored + return edges[id].lo + mf.edge_flow(edges[id].ref); + } + ll add_demands(vector &aux) { + // goal: connect ss/tt for feasible circulation + ll total = 0; + int ss = n, tt = n + 1; + for (int i = 0; i < n; i++) { + if (demand[i] > 0) { + aux.push_back(mf.add_edge(ss, i, demand[i], 0)); + total += demand[i]; + } else if (demand[i] < 0) { + aux.push_back(mf.add_edge(i, tt, -demand[i], 0)); + } + } + return total; + } + + pair feasible(bool init_pot = true) { + vector aux; + aux.reserve(n); + ll total = add_demands(aux); + int ss = n, tt = n + 1; + pll res = mf.min_cost_mf(ss, tt, total, init_pot); + for (auto ref : aux) mf.clear_edge(ref); + if (res.fr != total) return {false, 0}; + return {true, res.sc + base_cost}; + } + pair max_flow(int s, int t, bool init_pot = true) { + if (s == t) { + auto r = feasible(init_pot); + return {r.fr, {0, r.sc}}; + } + vector aux; + aux.reserve(n + 1); + int ss = n, tt = n + 1; + auto ts = mf.add_edge(t, s, INF, 0); + ll total = add_demands(aux); + pll res = mf.min_cost_mf(ss, tt, total, init_pot); + if (res.fr != total) { + mf.clear_edge(ts); + for (auto ref : aux) mf.clear_edge(ref); + return {false, {0, 0}}; + } + ll base = mf.edge_flow(ts); + ll cost = res.sc; + mf.clear_edge(ts); + for (auto ref : aux) mf.clear_edge(ref); + pll extra = mf.min_cost_mf(s, t, INF, init_pot); + return {true, {base + extra.fr, cost + extra.sc + base_cost}}; + } +}; diff --git a/src/5-string/rabin_karp_algorithm.cpp b/src/5-string/hash_rk.cpp similarity index 100% rename from src/5-string/rabin_karp_algorithm.cpp rename to src/5-string/hash_rk.cpp diff --git a/src/5-string/aho_corasick.cpp b/src/5-string/match_ac.cpp similarity index 100% rename from src/5-string/aho_corasick.cpp rename to src/5-string/match_ac.cpp diff --git a/src/5-string/kmp_algorithm.cpp b/src/5-string/match_kmp.cpp similarity index 100% rename from src/5-string/kmp_algorithm.cpp rename to src/5-string/match_kmp.cpp diff --git a/src/5-string/z_algorithm.cpp b/src/5-string/match_z.cpp similarity index 100% rename from src/5-string/z_algorithm.cpp rename to src/5-string/match_z.cpp diff --git a/src/5-string/manachers_algorithm.cpp b/src/5-string/pal_manacher.cpp similarity index 100% rename from src/5-string/manachers_algorithm.cpp rename to src/5-string/pal_manacher.cpp diff --git a/src/5-string/trie.cpp b/src/5-string/str_trie.cpp similarity index 100% rename from src/5-string/trie.cpp rename to src/5-string/str_trie.cpp diff --git a/src/5-string/suffix_array.cpp b/src/5-string/suffix_sa.cpp similarity index 100% rename from src/5-string/suffix_array.cpp rename to src/5-string/suffix_sa.cpp diff --git a/src/6-geometry/sort_by_angular.cpp b/src/6-geometry/geom_ang_sort.cpp similarity index 100% rename from src/6-geometry/sort_by_angular.cpp rename to src/6-geometry/geom_ang_sort.cpp diff --git a/src/6-geometry/bulldozer_trick.cpp b/src/6-geometry/geom_bulldozer.cpp similarity index 100% rename from src/6-geometry/bulldozer_trick.cpp rename to src/6-geometry/geom_bulldozer.cpp diff --git a/src/6-geometry/rotating_callipers.cpp b/src/6-geometry/geom_calipers.cpp similarity index 100% rename from src/6-geometry/rotating_callipers.cpp rename to src/6-geometry/geom_calipers.cpp diff --git a/src/6-geometry/half_plane_intersection.cpp b/src/6-geometry/geom_hpi.cpp similarity index 100% rename from src/6-geometry/half_plane_intersection.cpp rename to src/6-geometry/geom_hpi.cpp diff --git a/src/6-geometry/convex_hull.cpp b/src/6-geometry/geom_hull.cpp similarity index 100% rename from src/6-geometry/convex_hull.cpp rename to src/6-geometry/geom_hull.cpp diff --git a/src/6-geometry/minimum_enclosing_circle.cpp b/src/6-geometry/geom_mec.cpp similarity index 100% rename from src/6-geometry/minimum_enclosing_circle.cpp rename to src/6-geometry/geom_mec.cpp diff --git a/src/6-geometry/ray_casting.cpp b/src/6-geometry/geom_raycast.cpp similarity index 100% rename from src/6-geometry/ray_casting.cpp rename to src/6-geometry/geom_raycast.cpp diff --git a/src/6-geometry/segment_intersection.cpp b/src/6-geometry/geom_seg_inter.cpp similarity index 100% rename from src/6-geometry/segment_intersection.cpp rename to src/6-geometry/geom_seg_inter.cpp diff --git a/src/8-misc/system_of_difference_constraints.cpp b/src/8-misc/diff_cons.cpp similarity index 100% rename from src/8-misc/system_of_difference_constraints.cpp rename to src/8-misc/diff_cons.cpp diff --git a/src/8-misc/fraction_data_type.cpp b/src/8-misc/num_frac.cpp similarity index 100% rename from src/8-misc/fraction_data_type.cpp rename to src/8-misc/num_frac.cpp diff --git a/src/8-misc/kitamasa.cpp b/src/8-misc/rec_kitamasa.cpp similarity index 100% rename from src/8-misc/kitamasa.cpp rename to src/8-misc/rec_kitamasa.cpp diff --git a/src/8-misc/random.cpp b/src/8-misc/rnd.cpp similarity index 100% rename from src/8-misc/random.cpp rename to src/8-misc/rnd.cpp diff --git a/src/8-misc/ternary_search.cpp b/src/8-misc/search_ternary.cpp similarity index 100% rename from src/8-misc/ternary_search.cpp rename to src/8-misc/search_ternary.cpp diff --git a/src/8-misc/lis_in_o_nlogn.cpp b/src/8-misc/seq_lis.cpp similarity index 100% rename from src/8-misc/lis_in_o_nlogn.cpp rename to src/8-misc/seq_lis.cpp diff --git a/src/8-misc/sqrt_decomposition_mos_algorithm.cpp b/src/8-misc/sqrt_mo.cpp similarity index 100% rename from src/8-misc/sqrt_decomposition_mos_algorithm.cpp rename to src/8-misc/sqrt_mo.cpp diff --git a/tests/1-ds/test_union_find.cpp b/tests/1-ds/test_ds_dsu.cpp similarity index 95% rename from tests/1-ds/test_union_find.cpp rename to tests/1-ds/test_ds_dsu.cpp index d699179..6cb8afc 100644 --- a/tests/1-ds/test_union_find.cpp +++ b/tests/1-ds/test_ds_dsu.cpp @@ -1,9 +1,9 @@ -#include "../../src/1-ds/union_find.cpp" +#include "../../src/1-ds/ds_dsu.cpp" // what: tests for dsu. // time: random + edge cases; memory: O(n) // constraint: uses assert, fixed seed. -// usage: g++ -std=c++17 test_union_find.cpp && ./a.out +// usage: g++ -std=c++17 test_ds_dsu.cpp && ./a.out mt19937_64 rng(7); ll rnd(ll l, ll r) { diff --git a/tests/1-ds/test_erasable_pq.cpp b/tests/1-ds/test_ds_erasable_pq.cpp similarity index 94% rename from tests/1-ds/test_erasable_pq.cpp rename to tests/1-ds/test_ds_erasable_pq.cpp index 0f26519..47e912e 100644 --- a/tests/1-ds/test_erasable_pq.cpp +++ b/tests/1-ds/test_ds_erasable_pq.cpp @@ -1,9 +1,9 @@ -#include "../../src/1-ds/erasable_pq.cpp" +#include "../../src/1-ds/ds_erasable_pq.cpp" // what: tests for erase_pq (erasable pq). // time: random + edge cases; memory: O(n) // constraint: uses assert, fixed seed. -// usage: g++ -std=c++17 test_erasable_pq.cpp && ./a.out +// usage: g++ -std=c++17 test_ds_erasable_pq.cpp && ./a.out mt19937_64 rng(1); ll rnd(ll l, ll r) { diff --git a/tests/1-ds/test_fenwick_tree.cpp b/tests/1-ds/test_ds_fenwick.cpp similarity index 97% rename from tests/1-ds/test_fenwick_tree.cpp rename to tests/1-ds/test_ds_fenwick.cpp index afec125..77f7b4c 100644 --- a/tests/1-ds/test_fenwick_tree.cpp +++ b/tests/1-ds/test_ds_fenwick.cpp @@ -1,9 +1,9 @@ -#include "../../src/1-ds/fenwick_tree.cpp" +#include "../../src/1-ds/ds_fenwick.cpp" // what: tests for fenwick, fenw_range, fenw_2d. // time: random + edge cases; memory: O(n^2) // constraint: uses assert, fixed seed. -// usage: g++ -std=c++17 test_fenwick_tree.cpp && ./a.out +// usage: g++ -std=c++17 test_ds_fenwick.cpp && ./a.out mt19937_64 rng(2); ll rnd(ll l, ll r) { diff --git a/tests/1-ds/test_li_chao_tree.cpp b/tests/1-ds/test_ds_li_chao.cpp similarity index 93% rename from tests/1-ds/test_li_chao_tree.cpp rename to tests/1-ds/test_ds_li_chao.cpp index da90d97..5118a49 100644 --- a/tests/1-ds/test_li_chao_tree.cpp +++ b/tests/1-ds/test_ds_li_chao.cpp @@ -1,9 +1,9 @@ -#include "../../src/1-ds/li_chao_tree.cpp" +#include "../../src/1-ds/ds_li_chao.cpp" // what: tests for li_chao (max of lines on integer x-range). // time: random + edge cases; memory: O(n) // constraint: uses assert, fixed seed. -// usage: g++ -std=c++17 test_li_chao_tree.cpp && ./a.out +// usage: g++ -std=c++17 test_ds_li_chao.cpp && ./a.out mt19937_64 rng(6); ll rnd(ll l, ll r) { diff --git a/tests/1-ds/test_merge_sort_tree.cpp b/tests/1-ds/test_ds_merge_seg.cpp similarity index 94% rename from tests/1-ds/test_merge_sort_tree.cpp rename to tests/1-ds/test_ds_merge_seg.cpp index 5d8ef3b..bdd27a2 100644 --- a/tests/1-ds/test_merge_sort_tree.cpp +++ b/tests/1-ds/test_ds_merge_seg.cpp @@ -1,9 +1,9 @@ -#include "../../src/1-ds/merge_sort_tree.cpp" +#include "../../src/1-ds/ds_merge_seg.cpp" // what: tests for merge_seg, merge_seg_it. // time: random + edge cases; memory: O(n log n) // constraint: uses assert, fixed seed. -// usage: g++ -std=c++17 test_merge_sort_tree.cpp && ./a.out +// usage: g++ -std=c++17 test_ds_merge_seg.cpp && ./a.out mt19937_64 rng(3); ll rnd(ll l, ll r) { diff --git a/tests/1-ds/test_pbds.cpp b/tests/1-ds/test_ds_pbds.cpp similarity index 96% rename from tests/1-ds/test_pbds.cpp rename to tests/1-ds/test_ds_pbds.cpp index 558574b..9052ebd 100644 --- a/tests/1-ds/test_pbds.cpp +++ b/tests/1-ds/test_ds_pbds.cpp @@ -1,9 +1,9 @@ -#include "../../src/1-ds/pbds.cpp" +#include "../../src/1-ds/ds_pbds.cpp" // what: tests for pbds ordered set/multiset with order stats. // time: random + edge cases; memory: O(n) // constraint: uses assert, fixed seed. -// usage: g++ -std=c++17 test_pbds.cpp && ./a.out +// usage: g++ -std=c++17 test_ds_pbds.cpp && ./a.out mt19937_64 rng(11); ll rnd(ll l, ll r) { diff --git a/tests/1-ds/test_segment_tree.cpp b/tests/1-ds/test_ds_segtree.cpp similarity index 77% rename from tests/1-ds/test_segment_tree.cpp rename to tests/1-ds/test_ds_segtree.cpp index bd76df2..afeab84 100644 --- a/tests/1-ds/test_segment_tree.cpp +++ b/tests/1-ds/test_ds_segtree.cpp @@ -1,9 +1,9 @@ -#include "../../src/1-ds/segment_tree.cpp" +#include "../../src/1-ds/ds_segtree.cpp" -// what: tests for seg_tree, seg_tree_it, seg_tree_kth, seg_tree_lz, seg_pst, seg_sparse, seg_2d, seg2d_comp. +// what: tests for seg_tree, seg_tree_it, seg_tree_kth, seg_tree_lz, seg_sparse, seg_2d, seg2d_comp. // time: random + edge cases; memory: O(n log n) // constraint: uses assert, fixed seed. -// usage: g++ -std=c++17 test_segment_tree.cpp && ./a.out +// usage: g++ -std=c++17 test_ds_segtree.cpp && ./a.out mt19937_64 rng(4); ll rnd(ll l, ll r) { @@ -158,92 +158,6 @@ void test_seglz_random() { } } -void test_pst_basic() { - int n = 3; - vector a = {0, 1, 2, 3}; - seg_pst st; - st.build(n, a); - st.set(2, 5); - assert(st.query(1, 3, 0) == 6); - assert(st.query(1, 3, 1) == 9); -} - -void test_pst_random() { - int n = 20; - vector> ver; - vector a(n + 1, 0); - for (int i = 1; i <= n; i++) a[i] = rnd(-5, 5); - ver.push_back(a); - - seg_pst st; - st.build(n, a); - - for (int it = 0; it < 2000; it++) { - int op = (int)rnd(0, 1); - if (op == 0) { - int p = (int)rnd(1, n); - ll v = rnd(-5, 5); - vector nw = ver.back(); - nw[p] = v; - ver.push_back(nw); - st.set(p, v); - } else { - int id = (int)rnd(0, sz(ver) - 1); - int l = (int)rnd(1, n); - int r = (int)rnd(l, n); - assert(st.query(l, r, id) == sum_range(ver[id], l, r)); - } - } -} - -void test_pst_kth_basic() { - int n = 5; - vector a = {0, 2, 0, 1, 3, 0}; - seg_pst st; - st.build(n, a); - assert(st.kth(1, 0) == 1); - assert(st.kth(2, 0) == 1); - assert(st.kth(3, 0) == 3); - assert(st.kth(6, 0) == 4); - st.set(2, 4); - assert(st.kth(3, 1) == 2); - assert(st.kth(10, 1) == 4); - st.set(4, 0); - assert(st.kth(7, 2) == 3); -} - -void test_pst_kth_random() { - int n = 40; - vector> ver; - vector a(n + 1, 0); - for (int i = 1; i <= n; i++) a[i] = rnd(0, 3); - ver.push_back(a); - - seg_pst st; - st.build(n, a); - - for (int it = 0; it < 2000; it++) { - int op = (int)rnd(0, 1); - if (op == 0) { - int p = (int)rnd(1, n); - ll v = rnd(0, 5); - vector nw = ver.back(); - nw[p] = v; - ver.push_back(nw); - st.set(p, v); - } else { - int id = (int)rnd(0, sz(ver) - 1); - ll tot = 0; - for (int i = 1; i <= n; i++) tot += ver[id][i]; - if (tot == 0) continue; - ll k = rnd(1, tot); - assert(st.kth(k, id) == kth_naive_freq(ver[id], k)); - assert(st.kth(1, id) == kth_naive_freq(ver[id], 1)); - assert(st.kth(tot, id) == kth_naive_freq(ver[id], tot)); - } - } -} - void test_dyseg_basic() { seg_sparse st; st.add(MAXL, 5); @@ -414,10 +328,6 @@ int main() { test_segk_random(); test_seglz_basic(); test_seglz_random(); - test_pst_basic(); - test_pst_random(); - test_pst_kth_basic(); - test_pst_kth_random(); test_dyseg_basic(); test_dyseg_random(); test_seg2d_basic(); diff --git a/tests/1-ds/test_ds_segtree_pst.cpp b/tests/1-ds/test_ds_segtree_pst.cpp new file mode 100644 index 0000000..7b91924 --- /dev/null +++ b/tests/1-ds/test_ds_segtree_pst.cpp @@ -0,0 +1,121 @@ +#include "../../src/1-ds/ds_segtree_pst.cpp" + +// what: tests for seg_pst (persistent segment tree). +// time: random + edge cases; memory: O(n log n) +// constraint: fixed seed. +// usage: g++ -std=c++17 test_ds_segtree_pst.cpp && ./a.out + +mt19937_64 rng(4); +ll rnd(ll l, ll r) { + uniform_int_distribution dis(l, r); + return dis(rng); +} + +ll sum_range(const vector &a, int l, int r) { + ll ret = 0; + for (int i = l; i <= r; i++) ret += a[i]; + return ret; +} + +int kth_naive_freq(const vector &a, ll k) { + ll cur = 0; + for (int i = 1; i < sz(a); i++) { + cur += a[i]; + if (cur >= k) return i; + } + return -1; +} + +void test_pst_basic() { + int n = 3; + vector a = {0, 1, 2, 3}; + seg_pst st; + st.build(n, a); + st.set(2, 5); + assert(st.query(1, 3, 0) == 6); + assert(st.query(1, 3, 1) == 9); +} + +void test_pst_random() { + int n = 20; + vector> ver; + vector a(n + 1, 0); + for (int i = 1; i <= n; i++) a[i] = rnd(-5, 5); + ver.push_back(a); + + seg_pst st; + st.build(n, a); + + for (int it = 0; it < 2000; it++) { + int op = (int)rnd(0, 1); + if (op == 0) { + int p = (int)rnd(1, n); + ll v = rnd(-5, 5); + vector nw = ver.back(); + nw[p] = v; + ver.push_back(nw); + st.set(p, v); + } else { + int id = (int)rnd(0, sz(ver) - 1); + int l = (int)rnd(1, n); + int r = (int)rnd(l, n); + assert(st.query(l, r, id) == sum_range(ver[id], l, r)); + } + } +} + +void test_pst_kth_basic() { + int n = 5; + vector a = {0, 2, 0, 1, 3, 0}; + seg_pst st; + st.build(n, a); + assert(st.kth(1, 0) == 1); + assert(st.kth(2, 0) == 1); + assert(st.kth(3, 0) == 3); + assert(st.kth(6, 0) == 4); + st.set(2, 4); + assert(st.kth(3, 1) == 2); + assert(st.kth(10, 1) == 4); + st.set(4, 0); + assert(st.kth(7, 2) == 3); +} + +void test_pst_kth_random() { + int n = 40; + vector> ver; + vector a(n + 1, 0); + for (int i = 1; i <= n; i++) a[i] = rnd(0, 3); + ver.push_back(a); + + seg_pst st; + st.build(n, a); + + for (int it = 0; it < 2000; it++) { + int op = (int)rnd(0, 1); + if (op == 0) { + int p = (int)rnd(1, n); + ll v = rnd(0, 5); + vector nw = ver.back(); + nw[p] = v; + ver.push_back(nw); + st.set(p, v); + } else { + int id = (int)rnd(0, sz(ver) - 1); + ll tot = 0; + for (int i = 1; i <= n; i++) tot += ver[id][i]; + if (tot == 0) continue; + ll k = rnd(1, tot); + assert(st.kth(k, id) == kth_naive_freq(ver[id], k)); + assert(st.kth(1, id) == kth_naive_freq(ver[id], 1)); + assert(st.kth(tot, id) == kth_naive_freq(ver[id], tot)); + } + } +} + +int main() { + test_pst_basic(); + test_pst_random(); + test_pst_kth_basic(); + test_pst_kth_random(); + return 0; +} diff --git a/tests/2-graph/test_bcc.cpp b/tests/2-graph/test_cc_bcc.cpp similarity index 97% rename from tests/2-graph/test_bcc.cpp rename to tests/2-graph/test_cc_bcc.cpp index 0da8629..41a3fae 100644 --- a/tests/2-graph/test_bcc.cpp +++ b/tests/2-graph/test_cc_bcc.cpp @@ -1,9 +1,9 @@ -#include "../../src/2-graph/bcc.cpp" +#include "../../src/2-graph/cc_bcc.cpp" // what: tests for bcc_graph. // time: random + brute checks; memory: O(n+m) // constraint: small n brute. -// usage: g++ -std=c++17 test_bcc.cpp && ./a.out +// usage: g++ -std=c++17 test_cc_bcc.cpp && ./a.out mt19937_64 rng(1); int rnd(int l, int r) { diff --git a/tests/2-graph/test_scc_2_sat.cpp b/tests/2-graph/test_cc_scc.cpp similarity index 55% rename from tests/2-graph/test_scc_2_sat.cpp rename to tests/2-graph/test_cc_scc.cpp index 0593310..278eea1 100644 --- a/tests/2-graph/test_scc_2_sat.cpp +++ b/tests/2-graph/test_cc_scc.cpp @@ -1,9 +1,9 @@ -#include "../../src/2-graph/scc_2_sat.cpp" +#include "../../src/2-graph/cc_scc.cpp" -// what: tests for scc and 2-sat. +// what: tests for scc (kosaraju/tarjan). // time: random + brute; memory: O(n^2) // constraint: small n brute. -// usage: g++ -std=c++17 test_scc_2_sat.cpp && ./a.out +// usage: g++ -std=c++17 test_cc_scc.cpp && ./a.out mt19937_64 rng(4); int rnd(int l, int r) { @@ -41,26 +41,6 @@ vector cmp_na(int n, const vector> &g) { return cmp; } -bool sat_br(int n, const vector &cl, vector &val) { - int lim = 1 << n; - for (int mask = 0; mask < lim; mask++) { - bool ok = 1; - for (auto [a, b] : cl) { - bool va = (a > 0) ? ((mask >> (a - 1)) & 1) : !((mask >> (-a - 1)) & 1); - bool vb = (b > 0) ? ((mask >> (b - 1)) & 1) : !((mask >> (-b - 1)) & 1); - if (!(va || vb)) { - ok = 0; - break; - } - } - if (!ok) continue; - val.assign(n + 1, 0); - for (int i = 1; i <= n; i++) val[i] = (mask >> (i - 1)) & 1; - return 1; - } - return 0; -} - void t_scc() { for (int it = 0; it < 200; it++) { int n = rnd(1, 7); @@ -95,36 +75,7 @@ void t_scc() { } } -void t_sat() { - for (int it = 0; it < 200; it++) { - int n = rnd(1, 8); - int m = rnd(0, 12); - vector cl; - for (int i = 0; i < m; i++) { - int a = rnd(1, n), b = rnd(1, n); - if (rnd(0, 1)) a = -a; - if (rnd(0, 1)) b = -b; - cl.push_back({a, b}); - } - vector val; - bool ok2 = sat_br(n, cl, val); - - two_sat ts; - ts.init(n); - for (auto [a, b] : cl) ts.add(a, b); - bool ok1 = ts.run(); - assert(ok1 == ok2); - if (!ok1) continue; - for (auto [a, b] : cl) { - bool va = a > 0 ? ts.val[a] : !ts.val[-a]; - bool vb = b > 0 ? ts.val[b] : !ts.val[-b]; - assert(va || vb); - } - } -} - int main() { t_scc(); - t_sat(); return 0; } diff --git a/tests/2-graph/test_offline_dynamic_connectivity.cpp b/tests/2-graph/test_dyncon_off.cpp similarity index 95% rename from tests/2-graph/test_offline_dynamic_connectivity.cpp rename to tests/2-graph/test_dyncon_off.cpp index 416abec..0340320 100644 --- a/tests/2-graph/test_offline_dynamic_connectivity.cpp +++ b/tests/2-graph/test_dyncon_off.cpp @@ -1,9 +1,9 @@ -#include "../../src/2-graph/offline_dynamic_connectivity.cpp" +#include "../../src/2-graph/dyncon_off.cpp" // what: tests for offline dynamic connectivity. // time: random + brute; memory: O(n+q) // constraint: ops consistent; small n brute. -// usage: g++ -std=c++17 test_offline_dynamic_connectivity.cpp && ./a.out +// usage: g++ -std=c++17 test_dyncon_off.cpp && ./a.out mt19937_64 rng(6); int rnd(int l, int r) { diff --git a/tests/2-graph/test_euler_circuit.cpp b/tests/2-graph/test_euler_circ.cpp similarity index 96% rename from tests/2-graph/test_euler_circuit.cpp rename to tests/2-graph/test_euler_circ.cpp index 7f4795b..c030326 100644 --- a/tests/2-graph/test_euler_circuit.cpp +++ b/tests/2-graph/test_euler_circ.cpp @@ -1,9 +1,9 @@ -#include "../../src/2-graph/euler_circuit.cpp" +#include "../../src/2-graph/euler_circ.cpp" // what: tests for Euler circuit. // time: random + edge cases; memory: O(n^2) // constraint: small n brute. -// usage: g++ -std=c++17 test_euler_circuit.cpp && ./a.out +// usage: g++ -std=c++17 test_euler_circ.cpp && ./a.out mt19937_64 rng(2); int rnd(int l, int r) { diff --git a/tests/2-graph/test_sat_2sat.cpp b/tests/2-graph/test_sat_2sat.cpp new file mode 100644 index 0000000..cfc63aa --- /dev/null +++ b/tests/2-graph/test_sat_2sat.cpp @@ -0,0 +1,65 @@ +#include "../../src/2-graph/sat_2sat.cpp" + +// what: tests for two_sat. +// time: random + brute; memory: O(2^n) +// constraint: small n brute. +// usage: g++ -std=c++17 test_sat_2sat.cpp && ./a.out + +mt19937_64 rng(4); +int rnd(int l, int r) { + uniform_int_distribution dis(l, r); + return dis(rng); +} + +bool sat_br(int n, const vector &cl, vector &val) { + int lim = 1 << n; + for (int mask = 0; mask < lim; mask++) { + bool ok = 1; + for (auto [a, b] : cl) { + bool va = (a > 0) ? ((mask >> (a - 1)) & 1) : !((mask >> (-a - 1)) & 1); + bool vb = (b > 0) ? ((mask >> (b - 1)) & 1) : !((mask >> (-b - 1)) & 1); + if (!(va || vb)) { + ok = 0; + break; + } + } + if (!ok) continue; + val.assign(n + 1, 0); + for (int i = 1; i <= n; i++) val[i] = (mask >> (i - 1)) & 1; + return 1; + } + return 0; +} + +void t_sat() { + for (int it = 0; it < 200; it++) { + int n = rnd(1, 8); + int m = rnd(0, 12); + vector cl; + for (int i = 0; i < m; i++) { + int a = rnd(1, n), b = rnd(1, n); + if (rnd(0, 1)) a = -a; + if (rnd(0, 1)) b = -b; + cl.push_back({a, b}); + } + vector val; + bool ok2 = sat_br(n, cl, val); + + two_sat ts; + ts.init(n); + for (auto [a, b] : cl) ts.add(a, b); + bool ok1 = ts.run(); + assert(ok1 == ok2); + if (!ok1) continue; + for (auto [a, b] : cl) { + bool va = a > 0 ? ts.val[a] : !ts.val[-a]; + bool vb = b > 0 ? ts.val[b] : !ts.val[-b]; + assert(va || vb); + } + } +} + +int main() { + t_sat(); + return 0; +} diff --git a/tests/2-graph/test_shortest_path.cpp b/tests/2-graph/test_sp_basic.cpp similarity index 96% rename from tests/2-graph/test_shortest_path.cpp rename to tests/2-graph/test_sp_basic.cpp index 800dd76..12b8070 100644 --- a/tests/2-graph/test_shortest_path.cpp +++ b/tests/2-graph/test_sp_basic.cpp @@ -1,9 +1,9 @@ -#include "../../src/2-graph/shortest_path.cpp" +#include "../../src/2-graph/sp_basic.cpp" // what: tests for shortest paths. // time: random + brute; memory: O(n^2) // constraint: small n brute. -// usage: g++ -std=c++17 test_shortest_path.cpp && ./a.out +// usage: g++ -std=c++17 test_sp_basic.cpp && ./a.out mt19937_64 rng(3); int rnd(int l, int r) { diff --git a/tests/2-graph/test_kth_shortest_path.cpp b/tests/2-graph/test_sp_kth.cpp similarity index 93% rename from tests/2-graph/test_kth_shortest_path.cpp rename to tests/2-graph/test_sp_kth.cpp index 9ec3daa..1b7d0ca 100644 --- a/tests/2-graph/test_kth_shortest_path.cpp +++ b/tests/2-graph/test_sp_kth.cpp @@ -1,9 +1,9 @@ -#include "../../src/2-graph/kth_shortest_path.cpp" +#include "../../src/2-graph/sp_kth.cpp" // what: tests for k-th shortest walk. // time: random + brute; memory: O(n+m) // constraint: small n, positive weights. -// usage: g++ -std=c++17 test_kth_shortest_path.cpp && ./a.out +// usage: g++ -std=c++17 test_sp_kth.cpp && ./a.out mt19937_64 rng(5); int rnd(int l, int r) { diff --git a/tests/3-tree/test_lca_sparse_table.cpp b/tests/3-tree/test_lca_sparse.cpp similarity index 96% rename from tests/3-tree/test_lca_sparse_table.cpp rename to tests/3-tree/test_lca_sparse.cpp index d595a91..25a0d35 100644 --- a/tests/3-tree/test_lca_sparse_table.cpp +++ b/tests/3-tree/test_lca_sparse.cpp @@ -1,4 +1,4 @@ -#include "../../src/3-tree/lca_sparse_table.cpp" +#include "../../src/3-tree/lca_sparse.cpp" // what: tests for lca_sparse. // time: random + brute; memory: O(n^2) diff --git a/tests/3-tree/test_centroid_decomp.cpp b/tests/3-tree/test_tree_centroid.cpp similarity index 98% rename from tests/3-tree/test_centroid_decomp.cpp rename to tests/3-tree/test_tree_centroid.cpp index e15e797..4a4d2bf 100644 --- a/tests/3-tree/test_centroid_decomp.cpp +++ b/tests/3-tree/test_tree_centroid.cpp @@ -1,4 +1,4 @@ -#include "../../src/3-tree/centroid_decomp.cpp" +#include "../../src/3-tree/tree_centroid.cpp" // what: tests for cen_decomp. // time: random + brute; memory: O(n^2) diff --git a/tests/3-tree/test_hld.cpp b/tests/3-tree/test_tree_hld.cpp similarity index 98% rename from tests/3-tree/test_hld.cpp rename to tests/3-tree/test_tree_hld.cpp index bfe0a08..2f92e04 100644 --- a/tests/3-tree/test_hld.cpp +++ b/tests/3-tree/test_tree_hld.cpp @@ -1,4 +1,4 @@ -#include "../../src/3-tree/hld.cpp" +#include "../../src/3-tree/tree_hld.cpp" // what: tests for hld_tree (path sum). // time: random + brute; memory: O(n^2) diff --git a/tests/3-tree/test_tree_composition.cpp b/tests/3-tree/test_tree_vtree.cpp similarity index 98% rename from tests/3-tree/test_tree_composition.cpp rename to tests/3-tree/test_tree_vtree.cpp index 4baacff..44432d3 100644 --- a/tests/3-tree/test_tree_composition.cpp +++ b/tests/3-tree/test_tree_vtree.cpp @@ -1,4 +1,4 @@ -#include "../../src/3-tree/tree_composition.cpp" +#include "../../src/3-tree/tree_vtree.cpp" // what: tests for tree_comp (virtual tree builder). // time: random + brute; memory: O(n^2) diff --git a/tests/3-tree/test_tree_exchange_argument.cpp b/tests/3-tree/test_tree_xchg.cpp similarity index 98% rename from tests/3-tree/test_tree_exchange_argument.cpp rename to tests/3-tree/test_tree_xchg.cpp index 6a877eb..c325629 100644 --- a/tests/3-tree/test_tree_exchange_argument.cpp +++ b/tests/3-tree/test_tree_xchg.cpp @@ -1,4 +1,4 @@ -#include "../../src/3-tree/tree_exchange_argument.cpp" +#include "../../src/3-tree/tree_xchg.cpp" // what: tests for tree_xchg (exchange-argument greedy; brute checked). // time: random + brute; memory: O(n!) diff --git a/tests/4-optimizations/test_hungarian.cpp b/tests/4-optimizations/test_assign_hung.cpp similarity index 94% rename from tests/4-optimizations/test_hungarian.cpp rename to tests/4-optimizations/test_assign_hung.cpp index bbd518e..d2cddf1 100644 --- a/tests/4-optimizations/test_hungarian.cpp +++ b/tests/4-optimizations/test_assign_hung.cpp @@ -1,9 +1,9 @@ -#include "../../src/4-optimizations/hungarian.cpp" +#include "../../src/4-optimizations/assign_hung.cpp" // what: tests for hungarian (min assignment). // time: random + brute; memory: O(1) // constraint: small n,m. -// usage: g++ -std=c++17 test_hungarian.cpp && ./a.out +// usage: g++ -std=c++17 test_assign_hung.cpp && ./a.out mt19937_64 rng(402); int rnd(int l, int r) { diff --git a/tests/4-optimizations/test_flow_dinic.cpp b/tests/4-optimizations/test_flow_dinic.cpp new file mode 100644 index 0000000..01217ff --- /dev/null +++ b/tests/4-optimizations/test_flow_dinic.cpp @@ -0,0 +1,158 @@ +#include "../../src/4-optimizations/flow_dinic.cpp" + +// what: tests for dinic and lr_dinic. +// time: random + brute; memory: O(1) +// constraint: small n, small caps. +// usage: g++ -std=c++17 test_flow_dinic.cpp && ./a.out + +mt19937_64 rng(401); +int rnd(int l, int r) { + uniform_int_distribution dis(l, r); + return dis(rng); +} + +struct ek_flow { + struct edge { + int to, rev; + ll cap; + }; + int n; + vector> g; + void init(int n_) { + n = n_; + g.assign(n, {}); + } + void add_edge(int u, int v, ll cap) { + edge a{v, sz(g[v]), cap}; + edge b{u, sz(g[u]), 0}; + g[u].push_back(a); + g[v].push_back(b); + } + ll max_flow(int s, int t) { + if (s == t) return 0; + ll flow = 0; + while (1) { + vector pv(n, -1), pe(n, -1); + queue q; + q.push(s); + pv[s] = s; + while (!q.empty() && pv[t] == -1) { + int v = q.front(); + q.pop(); + for (int i = 0; i < sz(g[v]); i++) { + auto &e = g[v][i]; + if (!e.cap || pv[e.to] != -1) continue; + pv[e.to] = v; + pe[e.to] = i; + q.push(e.to); + if (e.to == t) break; + } + } + if (pv[t] == -1) break; + ll add = (1LL << 62); + for (int v = t; v != s; v = pv[v]) add = min(add, g[pv[v]][pe[v]].cap); + for (int v = t; v != s; v = pv[v]) { + auto &e = g[pv[v]][pe[v]]; + e.cap -= add; + g[v][e.rev].cap += add; + } + flow += add; + } + return flow; + } +}; + +struct lr_res { + bool ok; + ll flow, cost; +}; + +lr_res brute_lr(int n, int s, int t, const vector> &es) { + int m = sz(es); + vector f(m, 0); + ll best_f = -(1LL << 62), best_c = (1LL << 62); + function dfs = [&](int i) { + if (i == m) { + vector bal(n, 0); + ll cost = 0; + for (int k = 0; k < m; k++) { + auto [u, v, lo, hi, c] = es[k]; + bal[u] += f[k]; + bal[v] -= f[k]; + cost += 1LL * f[k] * c; + } + for (int v = 0; v < n; v++) + if (v != s && v != t && bal[v]) return; + if (bal[t] != -bal[s]) return; + ll flow = bal[s]; + if (flow < 0) return; + if (flow > best_f) best_f = flow, best_c = cost; + else if (flow == best_f) best_c = min(best_c, cost); + return; + } + auto [u, v, lo, hi, c] = es[i]; + for (int x = lo; x <= hi; x++) { + f[i] = x; + dfs(i + 1); + } + }; + dfs(0); + if (best_f == -(1LL << 62)) return {false, 0, 0}; + return {true, best_f, best_c}; +} + +void t_dinic() { + { + dinic mf(2); + mf.add_edge(0, 1, 3); + assert(mf.max_flow(0, 1) == 3); + } + for (int it = 0; it < 200; it++) { + int n = rnd(2, 10); + int m = rnd(1, min(25, n * (n - 1))); + int s = rnd(0, n - 1), t = rnd(0, n - 1); + dinic mf(n); + ek_flow na; + na.init(n); + for (int i = 0; i < m; i++) { + int u = rnd(0, n - 1), v = rnd(0, n - 1); + if (u == v) continue; + ll cap = rnd(0, 7); + mf.add_edge(u, v, cap); + na.add_edge(u, v, cap); + } + ll exp = na.max_flow(s, t); + ll lim = rnd(0, 10); + assert(mf.max_flow(s, t, lim) == min(exp, lim)); + } +} + +void t_lr_dinic() { + for (int it = 0; it < 200; it++) { + int n = rnd(2, 6); + int s = rnd(0, n - 1), t = rnd(0, n - 1); + while (t == s) t = rnd(0, n - 1); + int m = rnd(1, 8); + vector> es; + es.reserve(m); + lr_dinic f(n); + for (int i = 0; i < m; i++) { + int u = rnd(0, n - 1), v = rnd(0, n - 1); + if (u == v) continue; + int lo = rnd(0, 2); + int hi = lo + rnd(0, 2); + es.push_back({u, v, lo, hi, 0}); + f.add_edge(u, v, lo, hi); + } + auto exp = brute_lr(n, s, t, es); + auto got = f.max_flow(s, t); + assert(got.fr == exp.ok); + if (exp.ok) assert(got.sc == exp.flow); + } +} + +int main() { + t_dinic(); + t_lr_dinic(); + return 0; +} diff --git a/tests/4-optimizations/test_flow_hk.cpp b/tests/4-optimizations/test_flow_hk.cpp new file mode 100644 index 0000000..4f289c4 --- /dev/null +++ b/tests/4-optimizations/test_flow_hk.cpp @@ -0,0 +1,43 @@ +#include "../../src/4-optimizations/flow_hk.cpp" + +// what: tests for hk (Hopcroft-Karp). +// time: random + brute; memory: O(2^n) +// constraint: small n brute. +// usage: g++ -std=c++17 test_flow_hk.cpp && ./a.out + +mt19937_64 rng(401); +int rnd(int l, int r) { + uniform_int_distribution dis(l, r); + return dis(rng); +} + +int brute_match(int n_l, int n_r, const vector> &g) { + vector> dp(n_l + 1, vector(1 << n_r, -1)); + function go = [&](int i, int mask) { + if (i == n_l) return 0; + int &ret = dp[i][mask]; + if (ret != -1) return ret; + ret = go(i + 1, mask); + for (int r : g[i]) + if (!(mask & (1 << r))) ret = max(ret, 1 + go(i + 1, mask | (1 << r))); + return ret; + }; + return go(0, 0); +} + +void t_hk() { + for (int it = 0; it < 300; it++) { + int n_l = rnd(0, 10), n_r = rnd(0, 10); + hk bm(n_l, n_r); + vector> g(n_l); + for (int l = 0; l < n_l; l++) + for (int r = 0; r < n_r; r++) + if (rnd(0, 1)) bm.add_edge(l, r), g[l].push_back(r); + assert(bm.max_matching() == brute_match(n_l, n_r, g)); + } +} + +int main() { + t_hk(); + return 0; +} diff --git a/tests/4-optimizations/test_flow.cpp b/tests/4-optimizations/test_flow_mcmf.cpp similarity index 56% rename from tests/4-optimizations/test_flow.cpp rename to tests/4-optimizations/test_flow_mcmf.cpp index 055b53b..08d0e60 100644 --- a/tests/4-optimizations/test_flow.cpp +++ b/tests/4-optimizations/test_flow_mcmf.cpp @@ -1,9 +1,9 @@ -#include "../../src/4-optimizations/flow.cpp" +#include "../../src/4-optimizations/flow_mcmf.cpp" -// what: tests for flow templates (dinic/mcmf/matching/lower bounds). +// what: tests for mcmf and lr_mcmf. // time: random + brute; memory: O(1) // constraint: small n, small caps. -// usage: g++ -std=c++17 test_flow.cpp && ./a.out +// usage: g++ -std=c++17 test_flow_mcmf.cpp && ./a.out mt19937_64 rng(401); int rnd(int l, int r) { @@ -11,71 +11,6 @@ int rnd(int l, int r) { return dis(rng); } -struct ek_flow { - struct edge { - int to, rev; - ll cap; - }; - int n; - vector> g; - void init(int n_) { - n = n_; - g.assign(n, {}); - } - void add_edge(int u, int v, ll cap) { - edge a{v, sz(g[v]), cap}; - edge b{u, sz(g[u]), 0}; - g[u].push_back(a); - g[v].push_back(b); - } - ll max_flow(int s, int t) { - if (s == t) return 0; - ll flow = 0; - while (1) { - vector pv(n, -1), pe(n, -1); - queue q; - q.push(s); - pv[s] = s; - while (!q.empty() && pv[t] == -1) { - int v = q.front(); - q.pop(); - for (int i = 0; i < sz(g[v]); i++) { - auto &e = g[v][i]; - if (!e.cap || pv[e.to] != -1) continue; - pv[e.to] = v; - pe[e.to] = i; - q.push(e.to); - if (e.to == t) break; - } - } - if (pv[t] == -1) break; - ll add = (1LL << 62); - for (int v = t; v != s; v = pv[v]) add = min(add, g[pv[v]][pe[v]].cap); - for (int v = t; v != s; v = pv[v]) { - auto &e = g[pv[v]][pe[v]]; - e.cap -= add; - g[v][e.rev].cap += add; - } - flow += add; - } - return flow; - } -}; - -int brute_match(int n_l, int n_r, const vector> &g) { - vector> dp(n_l + 1, vector(1 << n_r, -1)); - function go = [&](int i, int mask) { - if (i == n_l) return 0; - int &ret = dp[i][mask]; - if (ret != -1) return ret; - ret = go(i + 1, mask); - for (int r : g[i]) - if (!(mask & (1 << r))) ret = max(ret, 1 + go(i + 1, mask | (1 << r))); - return ret; - }; - return go(0, 0); -} - struct mcmf_spfa { struct edge { int to, rev; @@ -177,44 +112,6 @@ lr_res brute_lr(int n, int s, int t, const vector return {true, best_f, best_c}; } -void t_dinic() { - { - dinic mf(2); - mf.add_edge(0, 1, 3); - assert(mf.max_flow(0, 1) == 3); - } - for (int it = 0; it < 200; it++) { - int n = rnd(2, 10); - int m = rnd(1, min(25, n * (n - 1))); - int s = rnd(0, n - 1), t = rnd(0, n - 1); - dinic mf(n); - ek_flow na; - na.init(n); - for (int i = 0; i < m; i++) { - int u = rnd(0, n - 1), v = rnd(0, n - 1); - if (u == v) continue; - ll cap = rnd(0, 7); - mf.add_edge(u, v, cap); - na.add_edge(u, v, cap); - } - ll exp = na.max_flow(s, t); - ll lim = rnd(0, 10); - assert(mf.max_flow(s, t, lim) == min(exp, lim)); - } -} - -void t_hk() { - for (int it = 0; it < 300; it++) { - int n_l = rnd(0, 10), n_r = rnd(0, 10); - hk bm(n_l, n_r); - vector> g(n_l); - for (int l = 0; l < n_l; l++) - for (int r = 0; r < n_r; r++) - if (rnd(0, 1)) bm.add_edge(l, r), g[l].push_back(r); - assert(bm.max_matching() == brute_match(n_l, n_r, g)); - } -} - void t_mcmf() { for (int it = 0; it < 200; it++) { int n = rnd(2, 8); @@ -246,30 +143,6 @@ void t_mcmf() { } } -void t_lr_dinic() { - for (int it = 0; it < 200; it++) { - int n = rnd(2, 6); - int s = rnd(0, n - 1), t = rnd(0, n - 1); - while (t == s) t = rnd(0, n - 1); - int m = rnd(1, 8); - vector> es; - es.reserve(m); - lr_dinic f(n); - for (int i = 0; i < m; i++) { - int u = rnd(0, n - 1), v = rnd(0, n - 1); - if (u == v) continue; - int lo = rnd(0, 2); - int hi = lo + rnd(0, 2); - es.push_back({u, v, lo, hi, 0}); - f.add_edge(u, v, lo, hi); - } - auto exp = brute_lr(n, s, t, es); - auto got = f.max_flow(s, t); - assert(got.fr == exp.ok); - if (exp.ok) assert(got.sc == exp.flow); - } -} - void t_lr_mcmf() { for (int it = 0; it < 150; it++) { int n = rnd(2, 6); @@ -296,10 +169,7 @@ void t_lr_mcmf() { } int main() { - t_dinic(); - t_hk(); t_mcmf(); - t_lr_dinic(); t_lr_mcmf(); return 0; } diff --git a/tests/5-string/test_rabin_karp_algorithm.cpp b/tests/5-string/test_hash_rk.cpp similarity index 87% rename from tests/5-string/test_rabin_karp_algorithm.cpp rename to tests/5-string/test_hash_rk.cpp index e50f849..6a86947 100644 --- a/tests/5-string/test_rabin_karp_algorithm.cpp +++ b/tests/5-string/test_hash_rk.cpp @@ -1,9 +1,9 @@ -#include "../../src/5-string/rabin_karp_algorithm.cpp" +#include "../../src/5-string/hash_rk.cpp" // what: tests for rk_match (rolling hash). // time: random + naive; memory: O(1) // constraint: small n. -// usage: g++ -std=c++17 test_rabin_karp_algorithm.cpp && ./a.out +// usage: g++ -std=c++17 test_hash_rk.cpp && ./a.out mt19937_64 rng(504); int rnd(int l, int r) { diff --git a/tests/5-string/test_aho_corasick.cpp b/tests/5-string/test_match_ac.cpp similarity index 92% rename from tests/5-string/test_aho_corasick.cpp rename to tests/5-string/test_match_ac.cpp index 96c243b..6bfa46c 100644 --- a/tests/5-string/test_aho_corasick.cpp +++ b/tests/5-string/test_match_ac.cpp @@ -1,9 +1,9 @@ -#include "../../src/5-string/aho_corasick.cpp" +#include "../../src/5-string/match_ac.cpp" // what: tests for aho_corasick (multi pattern match). // time: random + naive; memory: O(1) // constraint: lowercase strings. -// usage: g++ -std=c++17 test_aho_corasick.cpp && ./a.out +// usage: g++ -std=c++17 test_match_ac.cpp && ./a.out mt19937_64 rng(501); int rnd(int l, int r) { diff --git a/tests/5-string/test_kmp_algorithm.cpp b/tests/5-string/test_match_kmp.cpp similarity index 89% rename from tests/5-string/test_kmp_algorithm.cpp rename to tests/5-string/test_match_kmp.cpp index 9cb328e..3222bfa 100644 --- a/tests/5-string/test_kmp_algorithm.cpp +++ b/tests/5-string/test_match_kmp.cpp @@ -1,9 +1,9 @@ -#include "../../src/5-string/kmp_algorithm.cpp" +#include "../../src/5-string/match_kmp.cpp" // what: tests for kmp_match. // time: random + naive; memory: O(1) // constraint: none. -// usage: g++ -std=c++17 test_kmp_algorithm.cpp && ./a.out +// usage: g++ -std=c++17 test_match_kmp.cpp && ./a.out mt19937_64 rng(502); int rnd(int l, int r) { diff --git a/tests/5-string/test_z_algorithm.cpp b/tests/5-string/test_match_z.cpp similarity index 87% rename from tests/5-string/test_z_algorithm.cpp rename to tests/5-string/test_match_z.cpp index 23a855c..8b30769 100644 --- a/tests/5-string/test_z_algorithm.cpp +++ b/tests/5-string/test_match_z.cpp @@ -1,9 +1,9 @@ -#include "../../src/5-string/z_algorithm.cpp" +#include "../../src/5-string/match_z.cpp" // what: tests for z_func. // time: random + naive; memory: O(1) // constraint: small n. -// usage: g++ -std=c++17 test_z_algorithm.cpp && ./a.out +// usage: g++ -std=c++17 test_match_z.cpp && ./a.out mt19937_64 rng(507); int rnd(int l, int r) { diff --git a/tests/5-string/test_manachers_algorithm.cpp b/tests/5-string/test_pal_manacher.cpp similarity index 91% rename from tests/5-string/test_manachers_algorithm.cpp rename to tests/5-string/test_pal_manacher.cpp index 83a44bf..5b733f1 100644 --- a/tests/5-string/test_manachers_algorithm.cpp +++ b/tests/5-string/test_pal_manacher.cpp @@ -1,9 +1,9 @@ -#include "../../src/5-string/manachers_algorithm.cpp" +#include "../../src/5-string/pal_manacher.cpp" // what: tests for manacher (pal radii). // time: random + naive; memory: O(1) // constraint: small n. -// usage: g++ -std=c++17 test_manachers_algorithm.cpp && ./a.out +// usage: g++ -std=c++17 test_pal_manacher.cpp && ./a.out mt19937_64 rng(503); int rnd(int l, int r) { diff --git a/tests/5-string/test_trie.cpp b/tests/5-string/test_str_trie.cpp similarity index 87% rename from tests/5-string/test_trie.cpp rename to tests/5-string/test_str_trie.cpp index 8dd9184..dc2f5fb 100644 --- a/tests/5-string/test_trie.cpp +++ b/tests/5-string/test_str_trie.cpp @@ -1,9 +1,9 @@ -#include "../../src/5-string/trie.cpp" +#include "../../src/5-string/str_trie.cpp" // what: tests for trie (add/has). // time: random + naive; memory: O(1) // constraint: lowercase strings. -// usage: g++ -std=c++17 test_trie.cpp && ./a.out +// usage: g++ -std=c++17 test_str_trie.cpp && ./a.out mt19937_64 rng(506); int rnd(int l, int r) { diff --git a/tests/5-string/test_suffix_array.cpp b/tests/5-string/test_suffix_sa.cpp similarity index 92% rename from tests/5-string/test_suffix_array.cpp rename to tests/5-string/test_suffix_sa.cpp index d96bcfb..e5bc712 100644 --- a/tests/5-string/test_suffix_array.cpp +++ b/tests/5-string/test_suffix_sa.cpp @@ -1,9 +1,9 @@ -#include "../../src/5-string/suffix_array.cpp" +#include "../../src/5-string/suffix_sa.cpp" // what: tests for suffix_array (sa+lcp). // time: random + naive; memory: O(1) // constraint: small n. -// usage: g++ -std=c++17 test_suffix_array.cpp && ./a.out +// usage: g++ -std=c++17 test_suffix_sa.cpp && ./a.out mt19937_64 rng(505); int rnd(int l, int r) { diff --git a/tests/6-geometry/test_sort_by_angular.cpp b/tests/6-geometry/test_geom_ang_sort.cpp similarity index 94% rename from tests/6-geometry/test_sort_by_angular.cpp rename to tests/6-geometry/test_geom_ang_sort.cpp index 263b811..ac434a4 100644 --- a/tests/6-geometry/test_sort_by_angular.cpp +++ b/tests/6-geometry/test_geom_ang_sort.cpp @@ -1,9 +1,9 @@ -#include "../../src/6-geometry/sort_by_angular.cpp" +#include "../../src/6-geometry/geom_ang_sort.cpp" // what: tests for sort_ang (polar angle sort). // time: random + reference by atan2; memory: O(n) // constraint: fixed seed; avoids collinear rays in random. -// usage: g++ -std=c++17 test_sort_by_angular.cpp && ./a.out +// usage: g++ -std=c++17 test_geom_ang_sort.cpp && ./a.out mt19937_64 rng(7); ll rnd(ll l, ll r) { diff --git a/tests/6-geometry/test_bulldozer_trick.cpp b/tests/6-geometry/test_geom_bulldozer.cpp similarity index 96% rename from tests/6-geometry/test_bulldozer_trick.cpp rename to tests/6-geometry/test_geom_bulldozer.cpp index 489ba3a..edd5dc8 100644 --- a/tests/6-geometry/test_bulldozer_trick.cpp +++ b/tests/6-geometry/test_geom_bulldozer.cpp @@ -1,9 +1,9 @@ -#include "../../src/6-geometry/bulldozer_trick.cpp" +#include "../../src/6-geometry/geom_bulldozer.cpp" // what: tests for bulldozer (enumerates angular orders). // time: random + brute by sampling directions; memory: O(n^2) // constraint: fixed seed; uses long double directions in [0, pi). -// usage: g++ -std=c++17 test_bulldozer_trick.cpp && ./a.out +// usage: g++ -std=c++17 test_geom_bulldozer.cpp && ./a.out mt19937_64 rng(7); ll rnd(ll l, ll r) { diff --git a/tests/6-geometry/test_rotating_callipers.cpp b/tests/6-geometry/test_geom_calipers.cpp similarity index 87% rename from tests/6-geometry/test_rotating_callipers.cpp rename to tests/6-geometry/test_geom_calipers.cpp index 6bee9b2..f9f9073 100644 --- a/tests/6-geometry/test_rotating_callipers.cpp +++ b/tests/6-geometry/test_geom_calipers.cpp @@ -1,10 +1,10 @@ -#include "../../src/6-geometry/convex_hull.cpp" -#include "../../src/6-geometry/rotating_callipers.cpp" +#include "../../src/6-geometry/geom_calipers.cpp" +#include "../../src/6-geometry/geom_hull.cpp" // what: tests for hull_diam (rotating calipers). // time: random + brute; memory: O(n) // constraint: fixed seed; compares max dist2. -// usage: g++ -std=c++17 test_rotating_callipers.cpp && ./a.out +// usage: g++ -std=c++17 test_geom_calipers.cpp && ./a.out mt19937_64 rng(7); ll rnd(ll l, ll r) { diff --git a/tests/6-geometry/test_half_plane_intersection.cpp b/tests/6-geometry/test_geom_hpi.cpp similarity index 93% rename from tests/6-geometry/test_half_plane_intersection.cpp rename to tests/6-geometry/test_geom_hpi.cpp index 3a66732..5cc7411 100644 --- a/tests/6-geometry/test_half_plane_intersection.cpp +++ b/tests/6-geometry/test_geom_hpi.cpp @@ -1,10 +1,10 @@ -#include "../../src/6-geometry/convex_hull.cpp" -#include "../../src/6-geometry/half_plane_intersection.cpp" +#include "../../src/6-geometry/geom_hpi.cpp" +#include "../../src/6-geometry/geom_hull.cpp" // what: tests for hpi (half-plane intersection polygon). // time: random (convex) + edge cases; memory: O(n) // constraint: bounded cases only; eps checks. -// usage: g++ -std=c++17 test_half_plane_intersection.cpp && ./a.out +// usage: g++ -std=c++17 test_geom_hpi.cpp && ./a.out mt19937_64 rng(7); ll rnd(ll l, ll r) { diff --git a/tests/6-geometry/test_convex_hull.cpp b/tests/6-geometry/test_geom_hull.cpp similarity index 96% rename from tests/6-geometry/test_convex_hull.cpp rename to tests/6-geometry/test_geom_hull.cpp index 4232600..bb1d3c7 100644 --- a/tests/6-geometry/test_convex_hull.cpp +++ b/tests/6-geometry/test_geom_hull.cpp @@ -1,9 +1,9 @@ -#include "../../src/6-geometry/convex_hull.cpp" +#include "../../src/6-geometry/geom_hull.cpp" // what: tests for convex_hull (monotone chain, no collinear points on edges). // time: random + naive (jarvis); memory: O(n) // constraint: fixed seed; compares hull vertex set. -// usage: g++ -std=c++17 test_convex_hull.cpp && ./a.out +// usage: g++ -std=c++17 test_geom_hull.cpp && ./a.out mt19937_64 rng(7); ll rnd(ll l, ll r) { diff --git a/tests/6-geometry/test_minimum_enclosing_circle.cpp b/tests/6-geometry/test_geom_mec.cpp similarity index 95% rename from tests/6-geometry/test_minimum_enclosing_circle.cpp rename to tests/6-geometry/test_geom_mec.cpp index e4e8110..ae7377d 100644 --- a/tests/6-geometry/test_minimum_enclosing_circle.cpp +++ b/tests/6-geometry/test_geom_mec.cpp @@ -1,9 +1,9 @@ -#include "../../src/6-geometry/minimum_enclosing_circle.cpp" +#include "../../src/6-geometry/geom_mec.cpp" // what: tests for min_circle (minimum enclosing circle). // time: random + brute (n<=8); memory: O(n) // constraint: fixed seed; compares radius with eps. -// usage: g++ -std=c++17 test_minimum_enclosing_circle.cpp && ./a.out +// usage: g++ -std=c++17 test_geom_mec.cpp && ./a.out mt19937_64 rng(7); ll rnd(ll l, ll r) { diff --git a/tests/6-geometry/test_ray_casting.cpp b/tests/6-geometry/test_geom_raycast.cpp similarity index 92% rename from tests/6-geometry/test_ray_casting.cpp rename to tests/6-geometry/test_geom_raycast.cpp index 0e959ad..0535945 100644 --- a/tests/6-geometry/test_ray_casting.cpp +++ b/tests/6-geometry/test_geom_raycast.cpp @@ -1,10 +1,10 @@ -#include "../../src/6-geometry/convex_hull.cpp" -#include "../../src/6-geometry/ray_casting.cpp" +#include "../../src/6-geometry/geom_hull.cpp" +#include "../../src/6-geometry/geom_raycast.cpp" // what: tests for in_poly (ray casting, boundary included). // time: random (convex) + edge cases; memory: O(n) // constraint: fixed seed; convex reference via half-plane check. -// usage: g++ -std=c++17 test_ray_casting.cpp && ./a.out +// usage: g++ -std=c++17 test_geom_raycast.cpp && ./a.out mt19937_64 rng(7); ll rnd(ll l, ll r) { diff --git a/tests/6-geometry/test_segment_intersection.cpp b/tests/6-geometry/test_geom_seg_inter.cpp similarity index 94% rename from tests/6-geometry/test_segment_intersection.cpp rename to tests/6-geometry/test_geom_seg_inter.cpp index 1c5a7c3..a0bb2bc 100644 --- a/tests/6-geometry/test_segment_intersection.cpp +++ b/tests/6-geometry/test_geom_seg_inter.cpp @@ -1,9 +1,9 @@ -#include "../../src/6-geometry/segment_intersection.cpp" +#include "../../src/6-geometry/geom_seg_inter.cpp" // what: tests for seg_inter_p (intersection point). // time: random + edge cases; memory: O(1) // constraint: fixed seed; checks point-on-segment with eps. -// usage: g++ -std=c++17 test_segment_intersection.cpp && ./a.out +// usage: g++ -std=c++17 test_geom_seg_inter.cpp && ./a.out mt19937_64 rng(7); ll rnd(ll l, ll r) { diff --git a/tests/8-misc/test_system_of_difference_constraints.cpp b/tests/8-misc/test_diff_cons.cpp similarity index 92% rename from tests/8-misc/test_system_of_difference_constraints.cpp rename to tests/8-misc/test_diff_cons.cpp index d536a65..92b138a 100644 --- a/tests/8-misc/test_system_of_difference_constraints.cpp +++ b/tests/8-misc/test_diff_cons.cpp @@ -1,9 +1,9 @@ -#include "../../src/8-misc/system_of_difference_constraints.cpp" +#include "../../src/8-misc/diff_cons.cpp" // what: tests for difference constraints (negative cycle detection + solution). // time: random + floyd; memory: O(n^2) // constraint: fixed seed, small n. -// usage: g++ -std=c++17 test_system_of_difference_constraints.cpp && ./a.out +// usage: g++ -std=c++17 test_diff_cons.cpp && ./a.out mt19937_64 rng(2); int rnd_int(int l, int r) { diff --git a/tests/8-misc/test_fraction_data_type.cpp b/tests/8-misc/test_num_frac.cpp similarity index 93% rename from tests/8-misc/test_fraction_data_type.cpp rename to tests/8-misc/test_num_frac.cpp index 4ab72b0..109fff7 100644 --- a/tests/8-misc/test_fraction_data_type.cpp +++ b/tests/8-misc/test_num_frac.cpp @@ -1,9 +1,9 @@ -#include "../../src/8-misc/fraction_data_type.cpp" +#include "../../src/8-misc/num_frac.cpp" // what: tests for fraction (normalize, ops, comparisons). // time: random + edge cases; memory: O(1) // constraint: fixed seed, small values. -// usage: g++ -std=c++17 test_fraction_data_type.cpp && ./a.out +// usage: g++ -std=c++17 test_num_frac.cpp && ./a.out mt19937_64 rng(2); ll rnd(ll l, ll r) { diff --git a/tests/8-misc/test_kitamasa.cpp b/tests/8-misc/test_rec_kitamasa.cpp similarity index 91% rename from tests/8-misc/test_kitamasa.cpp rename to tests/8-misc/test_rec_kitamasa.cpp index 88fc9c4..15c849b 100644 --- a/tests/8-misc/test_kitamasa.cpp +++ b/tests/8-misc/test_rec_kitamasa.cpp @@ -1,9 +1,9 @@ -#include "../../src/8-misc/kitamasa.cpp" +#include "../../src/8-misc/rec_kitamasa.cpp" // what: tests for kitamasa linear recurrence. // time: random + naive; memory: O(k) // constraint: fixed seed, small n. -// usage: g++ -std=c++17 test_kitamasa.cpp && ./a.out +// usage: g++ -std=c++17 test_rec_kitamasa.cpp && ./a.out mt19937_64 rng(2); ll rnd(ll l, ll r) { diff --git a/tests/8-misc/test_ternary_search.cpp b/tests/8-misc/test_search_ternary.cpp similarity index 91% rename from tests/8-misc/test_ternary_search.cpp rename to tests/8-misc/test_search_ternary.cpp index facf045..72e5e36 100644 --- a/tests/8-misc/test_ternary_search.cpp +++ b/tests/8-misc/test_search_ternary.cpp @@ -1,9 +1,9 @@ -#include "../../src/8-misc/ternary_search.cpp" +#include "../../src/8-misc/search_ternary.cpp" // what: tests for ternary search (int + real). // time: random + brute; memory: O(1) // constraint: unimodal functions. -// usage: g++ -std=c++17 test_ternary_search.cpp && ./a.out +// usage: g++ -std=c++17 test_search_ternary.cpp && ./a.out ll f_int_max(ll x) { ll d = x - 7; diff --git a/tests/8-misc/test_lis_in_o_nlogn.cpp b/tests/8-misc/test_seq_lis.cpp similarity index 92% rename from tests/8-misc/test_lis_in_o_nlogn.cpp rename to tests/8-misc/test_seq_lis.cpp index 5bd5a7b..468be64 100644 --- a/tests/8-misc/test_lis_in_o_nlogn.cpp +++ b/tests/8-misc/test_seq_lis.cpp @@ -1,9 +1,9 @@ -#include "../../src/8-misc/lis_in_o_nlogn.cpp" +#include "../../src/8-misc/seq_lis.cpp" // what: tests for LIS length + sequence. // time: random + naive; memory: O(n^2) // constraint: fixed seed, small n. -// usage: g++ -std=c++17 test_lis_in_o_nlogn.cpp && ./a.out +// usage: g++ -std=c++17 test_seq_lis.cpp && ./a.out mt19937_64 rng(2); ll rnd(ll l, ll r) { diff --git a/tests/8-misc/test_sqrt_decomposition_mos_algorithm.cpp b/tests/8-misc/test_sqrt_mo.cpp similarity index 89% rename from tests/8-misc/test_sqrt_decomposition_mos_algorithm.cpp rename to tests/8-misc/test_sqrt_mo.cpp index 022118a..9641b26 100644 --- a/tests/8-misc/test_sqrt_decomposition_mos_algorithm.cpp +++ b/tests/8-misc/test_sqrt_mo.cpp @@ -1,9 +1,9 @@ -#include "../../src/8-misc/sqrt_decomposition_mos_algorithm.cpp" +#include "../../src/8-misc/sqrt_mo.cpp" // what: tests for mo's algorithm runner (distinct count). // time: random + naive; memory: O(n) // constraint: fixed seed, small values. -// usage: g++ -std=c++17 test_sqrt_decomposition_mos_algorithm.cpp && ./a.out +// usage: g++ -std=c++17 test_sqrt_mo.cpp && ./a.out mt19937_64 rng(2); int rnd_int(int l, int r) {