├── bin ├── run ├── allprob ├── casediff ├── makeprob ├── pipehack ├── downloadprob ├── interactive_runner ├── allprob.sh ├── makeprob.sh └── pipehack.sh ├── templates ├── cpp │ ├── input.txt │ └── code.cpp └── int │ └── code.cpp ├── code ├── test │ ├── datasets │ │ ├── mincost_circulation │ │ │ ├── .gitignore │ │ │ ├── make_large.sh │ │ │ └── google_or.min │ │ ├── mincost_flow.txt │ │ ├── dag.dot │ │ └── exact_tsp.txt │ ├── .gitignore │ ├── kahan.cpp │ ├── runall.sh │ ├── stable_matching.cpp │ ├── dynamic_cht.cpp │ ├── matrix.cpp │ ├── pwlc.cpp │ ├── quadratic.cpp │ ├── lca.cpp │ ├── triangulation.cpp │ ├── scc.cpp │ ├── binary_trie.cpp │ ├── Makefile │ ├── link_cut_tree_edge_path.cpp │ ├── aho_corasick.cpp │ ├── regular.cpp │ ├── centroid_decomposition.cpp │ ├── blackbox.cpp │ ├── topology.cpp │ ├── voronoi.cpp │ ├── travelling_salesman.cpp │ ├── cartesian_tree.cpp │ ├── 2sat.cpp │ ├── dominator_tree.cpp │ ├── runge_kutta.cpp │ ├── archive │ │ └── basic_bs_tree.cpp │ ├── mergesort_tree.cpp │ ├── wavelet_tree.cpp │ ├── ntt.cpp │ ├── mincost_matching.cpp │ ├── fft.cpp │ ├── test_utils.hpp │ ├── fft_split.cpp │ ├── simplex.cpp │ ├── binary_indexed_tree.cpp │ ├── combinatorics.cpp │ ├── mos.cpp │ ├── line_tree.cpp │ ├── heavy_light_decomposition.cpp │ ├── mincost_flow.cpp │ ├── delaunay.cpp │ ├── primes.cpp │ ├── halfplane.cpp │ ├── optimization.cpp │ ├── string_search.cpp │ ├── centroid_forest.cpp │ ├── slopy.cpp │ ├── tensor.cpp │ ├── compress.cpp │ ├── strings.cpp │ ├── bipartite_matching.cpp │ └── depth_first_tree.cpp ├── .gitignore ├── archive │ └── bstree │ │ └── tree_core.hpp ├── algo │ ├── y_combinator.hpp │ ├── iota_interpolation.hpp │ ├── cartesian_tree.hpp │ ├── compress.hpp │ ├── floor_sum.hpp │ ├── hornsat.hpp │ ├── virtual_tree.hpp │ ├── line_tree.hpp │ ├── mos.hpp │ └── optimization.hpp ├── matching │ ├── stable_matching.hpp │ ├── bipartite_matching.hpp │ └── hopcroft_karp.hpp ├── lib │ ├── strings.hpp │ ├── bigint_utils.hpp │ ├── anynum.hpp │ ├── flow.hpp │ ├── lr.hpp │ ├── 2sat.hpp │ ├── event_time_tracker.hpp │ ├── general_matching.hpp │ └── test_progress.hpp ├── stub │ └── hack.cpp ├── struct │ ├── persistent_array.hpp │ ├── min_stack.hpp │ ├── pbds.hpp │ ├── min_queue.hpp │ ├── dynamic_cht.hpp │ └── persistent_deque.hpp ├── numeric │ ├── partitions.hpp │ ├── int128.hpp │ ├── complex.hpp │ ├── kahan.hpp │ ├── chrono.hpp │ └── rangenum.hpp ├── geometry │ ├── duality2d.hpp │ ├── algo2d.hpp │ ├── format2d.hpp │ ├── format3d.hpp │ ├── algo3d.hpp │ ├── all_point_pairs.hpp │ └── halfplane.hpp ├── linear │ ├── berlekamp_massey.hpp │ ├── characteristic_polynomial.hpp │ └── matrix_cache.hpp ├── strings │ ├── z_search.hpp │ └── kmp.hpp ├── graphs │ ├── heavy_light_decomposition.hpp │ ├── travelling_salesman.hpp │ ├── centroid_decomposition.hpp │ ├── isomorphism.hpp │ ├── min_spanning_forest.hpp │ ├── shallow_decomposition.hpp │ ├── dominator_tree.hpp │ └── euler_tour.hpp ├── flow │ └── edmonds_karp.hpp └── parallel │ └── graph_orchestrator.hpp ├── .gitmodules ├── .editorconfig ├── README.md ├── .gitignore ├── LICENSE └── common ├── Makefile └── blog-graphs.txt /bin/run: -------------------------------------------------------------------------------- 1 | run.sh -------------------------------------------------------------------------------- /bin/allprob: -------------------------------------------------------------------------------- 1 | allprob.sh -------------------------------------------------------------------------------- /bin/casediff: -------------------------------------------------------------------------------- 1 | casediff.py -------------------------------------------------------------------------------- /bin/makeprob: -------------------------------------------------------------------------------- 1 | makeprob.sh -------------------------------------------------------------------------------- /bin/pipehack: -------------------------------------------------------------------------------- 1 | pipehack.sh -------------------------------------------------------------------------------- /bin/downloadprob: -------------------------------------------------------------------------------- 1 | downloadprob.py -------------------------------------------------------------------------------- /templates/cpp/input.txt: -------------------------------------------------------------------------------- 1 | 0 2 | -------------------------------------------------------------------------------- /bin/interactive_runner: -------------------------------------------------------------------------------- 1 | interactive_runner.py -------------------------------------------------------------------------------- /code/test/datasets/mincost_circulation/.gitignore: -------------------------------------------------------------------------------- 1 | *.large.min 2 | -------------------------------------------------------------------------------- /code/.gitignore: -------------------------------------------------------------------------------- 1 | test/*.txt 2 | TODO 3 | old.hpp 4 | Refactor.md 5 | old 6 | setting 7 | -------------------------------------------------------------------------------- /code/test/.gitignore: -------------------------------------------------------------------------------- 1 | code.cpp 2 | logs 3 | dep/ 4 | bin/ 5 | datasets/latest_error.txt 6 | output 7 | general 8 | geodebug 9 | -------------------------------------------------------------------------------- /code/test/datasets/mincost_circulation/make_large.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | for file in *.min.gz; do 4 | large=${file%.min.gz}.large.min.gz 5 | echo "$file" "$large" 6 | mv "$file" "$large" 7 | done 8 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "modules/fmt"] 2 | path = modules/fmt 3 | url = https://github.com/fmtlib/fmt 4 | [submodule "modules/matplotlib-cpp"] 5 | path = modules/matplotlib-cpp 6 | url = https://github.com/lava/matplotlib-cpp 7 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | # man 5 editorconfig-format 4 | 5 | [*] 6 | charset = utf-8 7 | end_of_line = lf 8 | indent_style = space 9 | indent_size = 4 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | 13 | [Makefile] 14 | indent_style = tab 15 | indent_size = 4 16 | -------------------------------------------------------------------------------- /templates/cpp/code.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #ifdef LOCAL 3 | #include "code/formatting.hpp" 4 | #else 5 | #define debug(...) (void)0 6 | #endif 7 | 8 | using namespace std; 9 | static_assert(sizeof(int) == 4 && sizeof(long) == 8); 10 | 11 | int main() { 12 | ios::sync_with_stdio(false), cin.tie(nullptr); 13 | return 0; 14 | } 15 | -------------------------------------------------------------------------------- /code/archive/bstree/tree_core.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "avl_tree.hpp" 4 | #include "rb_tree.hpp" 5 | #include "splay_tree.hpp" 6 | 7 | // Useful to make sure they all compile, at least 8 | 9 | template struct avl_tree; 10 | template struct rb_tree; 11 | template struct splay_tree; 12 | 13 | template 14 | using Tree = rb_tree; 15 | -------------------------------------------------------------------------------- /bin/allprob.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | declare -a FILES=('code.cpp' 'Makefile') 4 | 5 | for dir in $(find * -maxdepth 3 -type d | sort -n); do 6 | ok=1 7 | for file in "${FILES[@]}"; do 8 | if test ! -f "$dir/$file"; then 9 | ok=0 10 | break 11 | fi 12 | done 13 | if test "$ok" = 1; then 14 | echo "=== $dir" 15 | (cd "$dir" && "$@") 16 | fi 17 | done 18 | -------------------------------------------------------------------------------- /templates/int/code.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #ifdef LOCAL 3 | #include "code/formatting.hpp" 4 | #else 5 | #define debug(...) (void)0 6 | #endif 7 | 8 | using namespace std; 9 | static_assert(sizeof(int) == 4 && sizeof(long) == 8); 10 | 11 | int main() { 12 | ios::sync_with_stdio(true), setbuf(stdout, nullptr), cout.setf(ios::unitbuf); 13 | return 0; 14 | } 15 | -------------------------------------------------------------------------------- /code/test/datasets/mincost_circulation/google_or.min: -------------------------------------------------------------------------------- 1 | c Google OR example 2 | c https://developers.google.com/optimization/flow/mincostflow 3 | ans 150 4 | p min 5 9 5 | n 1 20 6 | n 2 0 7 | n 3 0 8 | n 4 -5 9 | n 5 -15 10 | a 1 2 0 15 4 11 | a 1 3 0 8 4 12 | a 2 3 0 20 2 13 | a 2 4 0 4 2 14 | a 2 5 0 10 6 15 | a 3 4 0 15 1 16 | a 3 5 0 4 3 17 | a 4 5 0 20 2 18 | a 5 3 0 5 3 19 | -------------------------------------------------------------------------------- /code/test/kahan.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utils.hpp" 2 | #include "numeric/kahan.hpp" 3 | 4 | void unit_test_kahan() { 5 | vector nums = {1e5, -1e6, 1e-7, -1e5, 1e6}; 6 | kahan k; 7 | for (double x : nums) { 8 | k += x; 9 | } 10 | println("k: {}", k.sum); 11 | } 12 | 13 | int main() { 14 | RUN_BLOCK(unit_test_kahan()); 15 | return 0; 16 | } 17 | -------------------------------------------------------------------------------- /bin/makeprob.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euf 4 | 5 | declare -r ROOT=$(run get-root) 6 | declare -r FOLDER="$1" 7 | declare -r NAME="${2:-cpp}" 8 | declare -r TMPL="$ROOT/templates/$NAME" 9 | 10 | if test -e "$FOLDER"; then 11 | echo "makeprob: File $FOLDER exists" >&2 12 | fi 13 | if test ! -d "$TMPL"; then 14 | echo "makeprob: Template $NAME not found" >&2 15 | exit 2 16 | fi 17 | 18 | cp -r "$TMPL" "$FOLDER" 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # competitive 2 | 3 | Generic programming library, [stress-tested](code/test) and [benchmarked](code/Benchmark.txt). Developed on the basis of need for contests. 4 | 5 | Highlights: 6 | 7 | - [Network simplex](code/flow/network_simplex.hpp) 8 | - [Micali vazirani](code/matching/micali_vazirani.hpp) 9 | - [Matroids](code/linear/matroids) 10 | - [D&C 3d hull](code/geometry/dachull.hpp) 11 | - [Top tree](code/struct/top_tree.hpp) 12 | -------------------------------------------------------------------------------- /code/test/datasets/mincost_flow.txt: -------------------------------------------------------------------------------- 1 | # https://developers.google.com/optimization/flow/mincostflow 2 | # with edges (3, cap=5, cost=0) and (4, cap=15, cost=0) to sink 5 3 | Google OR example 4 | 6 11 6 0 5 5 | 0 1 15 4 6 | 0 2 8 4 7 | 1 2 20 2 8 | 1 3 4 2 9 | 1 4 10 6 10 | 2 3 15 1 11 | 2 4 4 3 12 | 3 4 20 2 13 | 3 5 5 0 14 | 4 2 5 3 15 | 4 5 15 0 16 | 17 | 1 5 18 | 3 15 19 | 6 32 20 | 10 62 21 | 15 105 22 | 18 132 23 | 20 150 24 | -------------------------------------------------------------------------------- /code/test/runall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -eu 4 | 5 | declare -r FIRST="${1:-NONE}" 6 | declare FOUND=0 7 | if test "$FIRST" = "NONE"; then FOUND=1; fi 8 | 9 | set -eu -o pipefail 10 | mkdir -p ./output 11 | 12 | for file in bin/*; do 13 | if test "$FIRST" = "$file"; then FOUND=1; fi 14 | if test "$FOUND" -ne 1; then continue; fi 15 | file=${file#bin/} 16 | echo ===== "$file" 17 | /usr/bin/time -f "# %es $file" "./bin/$file" 18 | sleep 1s 19 | done 20 | -------------------------------------------------------------------------------- /code/test/datasets/dag.dot: -------------------------------------------------------------------------------- 1 | strict digraph { 2 | 0 -> 1 ; 3 | 0 -> 7 ; 4 | 1 -> 2 ; 5 | 1 -> 4 ; 6 | 2 -> 3 ; 7 | 2 -> 5 ; 8 | 2 -> 9 ; 9 | 3 -> 7 ; 10 | 4 -> 7 ; 11 | 4 -> 10 ; 12 | 5 -> 6 ; 13 | 5 -> 7 ; 14 | 5 -> 9 ; 15 | 6 -> 13 ; 16 | 6 -> 14 ; 17 | 7 -> 8 ; 18 | 7 -> 13 ; 19 | 8 -> 11 ; 20 | 9 -> 11 ; 21 | 10 -> 12 ; 22 | 10 -> 13 ; 23 | 12 -> 13 ; 24 | } 25 | -------------------------------------------------------------------------------- /code/test/stable_matching.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utils.hpp" 2 | #include "matching/stable_matching.hpp" 3 | 4 | void unit_test_stable_matching() { 5 | vector> up, vp; 6 | up = {{0, 1, 2}, {0, 2, 1}, {1, 0, 2}}; 7 | vp = {{0, 1, 2}, {2, 0, 1}, {2, 1, 0}}; 8 | 9 | auto [mu, mv] = stable_matching(up, vp); 10 | 11 | for (int u = 0, V = mu.size(); u < V; u++) { 12 | print("{} -- {}\n", u, mu[u]); 13 | } 14 | } 15 | 16 | int main() { 17 | RUN_BLOCK(unit_test_stable_matching()); 18 | return 0; 19 | } 20 | -------------------------------------------------------------------------------- /code/test/dynamic_cht.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utils.hpp" 2 | #include "struct/dynamic_cht.hpp" 3 | 4 | void unit_test_dynamic_cht() { 5 | dynamic_cht cht; 6 | cht.add(2, -3); 7 | cht.add(4, -70); 8 | cht.add(-20, -500); 9 | cht.add(-5, -100); 10 | cht.add(-8, -200); 11 | cht.add(10, -100); 12 | cht.add(-1, 100); 13 | cout << cht.size() << endl; 14 | for (auto [m, b, end] : cht) { 15 | print("({}, {}, {})\n", m, b, end); 16 | } 17 | } 18 | 19 | int main() { 20 | RUN_SHORT(unit_test_dynamic_cht()); 21 | return 0; 22 | } 23 | -------------------------------------------------------------------------------- /code/algo/y_combinator.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | using namespace std; 5 | 6 | template 7 | class y_combinator_result { 8 | Fun fun_; 9 | 10 | public: 11 | template 12 | explicit y_combinator_result(T&& fun) : fun_(std::forward(fun)) {} 13 | 14 | template 15 | decltype(auto) operator()(Args&&... args) { 16 | return fun_(std::ref(*this), std::forward(args)...); 17 | } 18 | }; 19 | 20 | template 21 | auto y_combinator(Fun&& fun) { 22 | return y_combinator_result>(std::forward(fun)); 23 | } 24 | -------------------------------------------------------------------------------- /bin/pipehack.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | declare -r SOLVER='./solver' HACKER='./hacker' 4 | declare -r INPUT='./input.txt' OUTPUT='./output.txt' 5 | declare -r PATTERN='^Case #\d+:' TRACE='^::hack' 6 | 7 | print_input_case() { 8 | local CASE=$(($1 + 1)); NEXT=$((CASE + 1)) 9 | awk "/$TRACE $NEXT\>/{flag=0} flag; /$TRACE $CASE\>/{flag=1}" "$INPUT" 10 | } 11 | 12 | main() { 13 | if "$HACKER" "$@" | tee "$INPUT" | grep -vP "$TRACE" | "$SOLVER" > "$OUTPUT"; then 14 | echo OK 15 | else 16 | CASES="$(grep -cPe "$PATTERN" "$OUTPUT")" 17 | print_input_case $((CASES + 1)) 18 | fi 19 | } 20 | 21 | test "${BASH_SOURCE[0]}" != "${0}" || main "$@" 22 | -------------------------------------------------------------------------------- /code/test/matrix.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utils.hpp" 2 | #include "linear/matrix.hpp" 3 | #include "linear/gauss.hpp" 4 | #include "linear/matrix_cache.hpp" 5 | #include "numeric/modnum.hpp" 6 | 7 | void unit_test_matrix() { 8 | using num = modnum<7>; 9 | mat a = mat::from({ 10 | {0, 3, 5, 1}, 11 | {2, 1, 4, 6}, 12 | {3, 0, 0, 2}, 13 | }); 14 | vector b = {1, 2, 3}; 15 | 16 | if (auto x = solve_linear_system(a, b); !x.empty()) { 17 | println("x: {}", x); 18 | assert(a * x == b); 19 | } else { 20 | println("ans: none"); 21 | } 22 | } 23 | 24 | int main() { 25 | RUN_BLOCK(unit_test_matrix()); 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | 9 | # Precompiled Headers 10 | *.gch 11 | *.pch 12 | 13 | # Compiled Dynamic libraries 14 | *.so 15 | *.dylib 16 | *.dll 17 | 18 | # Fortran module files 19 | *.mod 20 | *.smod 21 | 22 | # Compiled Static libraries 23 | *.lai 24 | *.la 25 | *.a 26 | *.lib 27 | 28 | # Executables 29 | *.exe 30 | *.out 31 | *.app 32 | solve 33 | solver 34 | hack 35 | hacker 36 | hack_generate 37 | hack_naive 38 | 39 | # Hidden files 40 | *.h.* 41 | 42 | # Dumps 43 | core 44 | vgcore* 45 | output.txt 46 | answer.txt 47 | program.txt 48 | debug.txt 49 | *.ans 50 | 51 | # Instrumentation 52 | gmon.out 53 | analysis.txt 54 | sandbox 55 | symbols.txt 56 | geodeb.h 57 | testlib.h 58 | -------------------------------------------------------------------------------- /code/matching/stable_matching.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | using namespace std; 5 | 6 | auto stable_matching(const vector>& u, const vector>& v) { 7 | int N = u.size(); 8 | vector mu(N, -1), mv(N, -1); 9 | for (int i = 0; i < N; i++) { 10 | int k = 0; 11 | while (mu[i] < 0) { 12 | int w = u[i][k++]; 13 | int m = mv[w]; 14 | if (m == -1) { 15 | mu[i] = w; 16 | mv[w] = i; 17 | } else if (v[w][i] < v[w][m]) { 18 | mu[m] = -1; 19 | mu[i] = w; 20 | mv[w] = i; 21 | i = m; 22 | } 23 | } 24 | } 25 | return make_pair(move(mu), move(mv)); 26 | } 27 | -------------------------------------------------------------------------------- /code/test/pwlc.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utils.hpp" 2 | #include "../struct/pwlc.hpp" 3 | 4 | void unit_test_pwlc() { 5 | using LP = LinPoints; 6 | LP a = LP::abs(3, 4, 0, 2); 7 | LP b = LP::abs(5, 2, 3, 1); 8 | LP c = LP::valley(2, 1, -1, 1, 4); 9 | putln("a", a.minimum(), a.left_argmin(), a.right_argmin()); 10 | putln("b", b.minimum(), b.left_argmin(), b.right_argmin()); 11 | putln("c", c.minimum(), c.left_argmin(), c.right_argmin()); 12 | LP::pointwise(a, b); 13 | putln("a+b", a.minimum(), a.left_argmin(), a.right_argmin()); 14 | LP::pointwise(a, c); 15 | putln("a+b+c", a.minimum(), a.left_argmin(), a.right_argmin()); 16 | putln("a+b+c(2)", a.destructive_query(2)); 17 | } 18 | 19 | int main() { 20 | RUN_BLOCK(unit_test_pwlc()); 21 | return 0; 22 | } 23 | -------------------------------------------------------------------------------- /code/lib/strings.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "random.hpp" 4 | 5 | // Generate all strings, with length [minlen,maxlen] with characters [a,b] 6 | auto generate_all_strings(int minlen, int maxlen, char a = 'a', char b = 'z') { 7 | assert(pow(b - a + 1, maxlen) < 1e8); 8 | vector strs{""}; 9 | int L = 0, S = 1, k = 1, splice_index = 0; 10 | for (int len = 1; len <= maxlen; len++) { 11 | if (len == minlen) 12 | splice_index = S; 13 | strs.resize(S + (b - a + 1) * (S - L)); 14 | for (char c = a; c <= b; c++) { 15 | for (int i = L; i < S; i++) { 16 | strs[k++] = strs[i] + c; 17 | } 18 | } 19 | L = S, S = k; 20 | } 21 | strs.erase(begin(strs), begin(strs) + splice_index); 22 | return strs; 23 | } 24 | -------------------------------------------------------------------------------- /code/stub/hack.cpp: -------------------------------------------------------------------------------- 1 | #include "../../code/hacking.hpp" 2 | 3 | int T = 100; 4 | vector input; 5 | 6 | int main(int argc, char** argv) { 7 | // if (argc >= 2) mt.seed(stoull(argv[1])), argc--, argv++; // seed mt 8 | // if (argc >= 2) T = stoi(argv[1]), argc--, argv++; // read a batch size 9 | // while (argc >= 2) input.push_back(stoll(argv[1])), argc--, argv++; // read numbers 10 | 11 | // ofstream ans("answer.txt"); 12 | putln(T); 13 | for (int t = 1; t <= T; t++) { 14 | // putln("::hack", t); 15 | int N = rand_int(1, 5); 16 | putln(N); 17 | for (int i = 0; i < N; i++) { 18 | int a = rand_int(1, 100'000); 19 | int b = rand_int(1, 100'000); 20 | int c = rand_int(1, 100'000); 21 | putln(a, b, c); 22 | } 23 | } 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /code/test/quadratic.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utils.hpp" 2 | #include "numeric/quadratic.hpp" 3 | #include "numeric/frac.hpp" 4 | 5 | void unit_test_quadratic() { 6 | using quad = quadratic>; 7 | quad f1(1, -2, 1); 8 | quad f2(1, 3, 2); 9 | quad f3(3, -7, 1); 10 | quad f4(1, 1, 1); 11 | quad f5(3, 8, -7); 12 | 13 | auto zeros = [&](auto f) { 14 | println("zeros({},{},{}) = {}\n", f.a, f.b, f.c, f.roots()); 15 | }; 16 | auto eval = [&](auto f, auto x) { 17 | println("f({})={}x^2+{}x+{}={}\n", x, f.a, f.b, f.c, f(x)); 18 | }; 19 | 20 | zeros(f1); 21 | zeros(f2); 22 | zeros(f3); 23 | zeros(f4); 24 | zeros(f5); 25 | eval(f1, 1); 26 | eval(f1, f1.argvertex()); 27 | eval(f1, f1.argmin(2, 4)); 28 | eval(f1, f1.argmax(2, 4)); 29 | } 30 | 31 | int main() { 32 | RUN_BLOCK(unit_test_quadratic()); 33 | return 0; 34 | } 35 | -------------------------------------------------------------------------------- /code/lib/bigint_utils.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "numeric/bigint.hpp" 4 | #include "random.hpp" 5 | 6 | string random_numeric_string(int digits, int base = 10, double neg_p = 0.0, 7 | bool zero = false) { 8 | static boold signd(0.3); 9 | boold negd(neg_p); 10 | 11 | string s = rand_string(digits, '0', '0' + base - 1); 12 | auto i = s.find_first_not_of('0'); 13 | if (i == string::npos) 14 | return zero ? "0" : "1"; 15 | s = s.substr(i); 16 | if (negd(mt)) 17 | return "-" + s; 18 | else if (signd(mt)) 19 | return "+" + s; 20 | else 21 | return s; 22 | } 23 | 24 | bigint random_bigint(int digits, int base = 10, double neg_p = 0.0, bool zero = false) { 25 | return bigint(random_numeric_string(digits, base, neg_p, zero), base); 26 | } 27 | 28 | bigint bigpow(int n, int base = 10) { return bigint("1" + string(n, '0'), base); } 29 | -------------------------------------------------------------------------------- /code/algo/iota_interpolation.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "numeric/combinatorics.hpp" 4 | 5 | // Given polynomial P such that P[0,...,n)=y[0,...,n), compute P(x). O(n) 6 | template 7 | auto interpolate_iota(const vector& y, int64_t x) { 8 | int N = y.size(); 9 | if (0 <= x && x < N) { 10 | return y[x]; 11 | } else if (N == 0) { 12 | return T(0); 13 | } 14 | 15 | using B = Binomial; 16 | B::ensure_factorial(N); 17 | 18 | vector dp(N, 1), pd(N, 1); 19 | for (int i = 0; i < N - 1; i++) { 20 | dp[i + 1] = dp[i] * (x - i); 21 | } 22 | for (int i = N - 1; i > 0; i--) { 23 | pd[i - 1] = pd[i] * (x - i); 24 | } 25 | 26 | T val = 0; 27 | for (int i = 0; i < N; i++) { 28 | T cell = y[i] * dp[i] * pd[i] * B::ifact[i] * B::ifact[N - i - 1]; 29 | val += ((N - i) & 1) ? cell : -cell; 30 | } 31 | return val; 32 | } 33 | -------------------------------------------------------------------------------- /code/lib/anynum.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "numeric/frac.hpp" 4 | #include "random.hpp" 5 | 6 | template 7 | O uniform_gen(long minv, long maxv, long maxd = 2) { 8 | assert(max(abs(minv), abs(maxv)) <= LONG_MAX / maxd); 9 | if constexpr (is_same_v>) { 10 | long d = longd(1, maxd)(mt); 11 | longd dist(minv * d, maxv * d); 12 | return frac(dist(mt), d); 13 | } else if constexpr (is_integral_v) { 14 | return longd(minv, maxv)(mt); 15 | } else if constexpr (is_arithmetic_v) { 16 | long n = longd(minv * maxd, maxv * maxd)(mt); 17 | return T(n) / T(maxd); 18 | } 19 | } 20 | 21 | template 22 | auto uniform_gen_many(int n, long minv, long maxv, long maxd = 2) { 23 | vector nums(n); 24 | for (int i = 0; i < n; i++) { 25 | nums[i] = uniform_gen(minv, maxv, maxd); 26 | } 27 | return nums; 28 | } 29 | -------------------------------------------------------------------------------- /code/lib/flow.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "random.hpp" 4 | 5 | template 6 | auto generate_feasible_circulation(int V, const vector>& g, 7 | array flowrange, int repulsion = -10) { 8 | int E = g.size(); 9 | auto flow1 = rands_wide(E, flowrange[0], flowrange[1], repulsion); 10 | auto flow2 = rands_wide(E, flowrange[0], flowrange[1], repulsion); 11 | auto flow3 = rands_wide(E, flowrange[0], flowrange[1], repulsion); 12 | vector lower(E), upper(E), flow(E), supply(V); 13 | for (int e = 0; e < E; e++) { 14 | lower[e] = min({flow1[e], flow2[e], flow3[e]}); 15 | upper[e] = max({flow1[e], flow2[e], flow3[e]}); 16 | flow[e] = lower[e] ^ upper[e] ^ flow1[e] ^ flow2[e] ^ flow3[e]; 17 | auto [u, v] = g[e]; 18 | supply[u] += flow[e]; 19 | supply[v] -= flow[e]; 20 | } 21 | return make_tuple(move(lower), move(upper), move(flow), move(supply)); 22 | } 23 | -------------------------------------------------------------------------------- /code/test/lca.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utils.hpp" 2 | #include "lib/graph_operations.hpp" 3 | #include "lib/graph_formats.hpp" 4 | #include "lib/graph_generator.hpp" 5 | #include "struct/lca.hpp" 6 | 7 | void unit_test_lca_tree() { 8 | int V = 20; 9 | string s = "1,2 1,3 1,4 1,5 2,6 2,7 3,8 3,9 3,10 5,11 5,12 5,13 " 10 | "7,14 10,15 10,16 13,17 13,18 13,19"; 11 | auto g = scan_edges(s); 12 | random_flip_graph_inplace(g); 13 | auto tree = make_adjacency_lists_undirected(V, g); 14 | 15 | lca_incremental lca(tree, 1); 16 | 17 | assert(lca.lca(11, 19) == 5); 18 | assert(lca.lca(9, 15) == 3); 19 | assert(lca.lca(14, 15) == 1); 20 | assert(lca.lca(11, 13) == 5); 21 | assert(lca.depth[8] == 2); 22 | assert(lca.depth[16] == 3); 23 | assert(lca.dist(7, 17) == 5); 24 | assert(lca.dist(6, 8) == 4); 25 | assert(lca.dist(3, 3) == 0); 26 | assert(lca.dist(3, 15) == 2); 27 | } 28 | 29 | int main() { 30 | RUN_BLOCK(unit_test_lca_tree()); 31 | return 0; 32 | } 33 | -------------------------------------------------------------------------------- /code/test/triangulation.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utils.hpp" 2 | #include "geometry/triangulation.hpp" 3 | #include "geometry/utils2d.hpp" 4 | #include "geometry/generator2d.hpp" 5 | 6 | void stress_test_constrained_triangulation() { 7 | LOOP_FOR_DURATION_OR_RUNS_TRACKED (30s, now, 20'000, runs) { 8 | print_time(now, 30s, "stress constrained triangulation {} runs", runs); 9 | 10 | int N = rand_unif(5, 150); 11 | int S = rand_unif(5, min(70, 2 * N)); 12 | auto dist = rand_point_distribution(); 13 | 14 | auto pts = generate_points(N, dist, 0); 15 | auto segments = non_overlapping_sample(pts, S, {}, false); 16 | 17 | Wedge* edge = constrained_triangulation(pts, segments); 18 | auto faces = Wedge::extract_faces(edge); 19 | 20 | assert(check_constrained_triangulation(faces, pts, segments)); 21 | Wedge::release(); 22 | } 23 | } 24 | 25 | int main() { 26 | RUN_BLOCK(stress_test_constrained_triangulation()); 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /code/test/scc.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utils.hpp" 2 | #include "lib/graph_formats.hpp" 3 | #include "graphs/scc.hpp" 4 | 5 | void unit_test_scc() { 6 | // vertex 0 is completely disconnected 7 | const int V = 9; 8 | edges_t g = scan_edges("1,2 2,3 3,1 4,2 4,3 4,6 5,3 5,7 6,4 6,5 7,5 8,6 8,7 8,8"); 9 | auto adj = make_adjacency_lists_directed(V, g); 10 | auto [C, cmap] = build_scc(adj); 11 | auto [sccedges, sccout, sccin] = condensate_scc(C, adj, cmap); 12 | 13 | assert(C == 5); // num components 14 | for (int c = 0; c < C; c++) { 15 | sort(begin(sccout[c]), end(sccout[c])); 16 | sort(begin(sccin[c]), end(sccin[c])); 17 | } 18 | 19 | using vi = vector; 20 | 21 | assert(sccout[0] == vi() && sccin[0] == vi()); 22 | assert(sccout[1] == vi() && sccin[1] == vi({2, 3})); 23 | assert(sccout[2] == vi({1}) && sccin[2] == vi({3, 4})); 24 | assert(sccout[3] == vi({1, 2}) && sccin[3] == vi({4})); 25 | assert(sccout[4] == vi({2, 3}) && sccin[4] == vi()); 26 | } 27 | 28 | int main() { 29 | RUN_BLOCK(unit_test_scc()); 30 | return 0; 31 | } 32 | -------------------------------------------------------------------------------- /code/test/binary_trie.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utils.hpp" 2 | #include "struct/binary_trie.hpp" 3 | 4 | void stress_test_xor_range() { 5 | constexpr int B = 9, MAX = 1 << B; 6 | binary_trie trie; 7 | multiset nums; 8 | 9 | uniform_int_distribution anynum(0, MAX - 1); 10 | 11 | for (int n = 0; n < MAX; n++) { 12 | int cnt = rand_unif(0, 4); 13 | trie.insert(n, cnt, cnt); 14 | for (int c = 0; c < cnt; c++) { 15 | nums.insert(n); 16 | } 17 | } 18 | 19 | LOOP_FOR_DURATION_TRACKED_RUNS (5s, now, runs) { 20 | print_time(now, 5s, "stress xor range ({} runs)", runs); 21 | 22 | int x = anynum(mt); 23 | auto [l, r] = diff_unif(0, MAX); 24 | int got = trie.count_xor_range(x, l, r); 25 | int exp = 0; 26 | for (int n : nums) { 27 | exp += l <= (n ^ x) && (n ^ x) < r; 28 | } 29 | assert(got == exp); 30 | assert(int(nums.count(x)) == trie.contains(x)); 31 | } 32 | } 33 | 34 | int main() { 35 | RUN_BLOCK(stress_test_xor_range()); 36 | return 0; 37 | } 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Bruno Carvalho 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /code/algo/cartesian_tree.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | using namespace std; 5 | 6 | // min cartesian tree for less (minimum on the root), lefts go higher 7 | template > 8 | auto cartesian_tree(const vector& arr, const Compare& cmp = Compare()) { 9 | int N = arr.size(); 10 | vector parent(N, -1); 11 | vector> kids(N, {-1, -1}); 12 | 13 | for (int i = 1; i < N; i++) { 14 | int p = i - 1; 15 | while (parent[p] != -1 && cmp(arr[i], arr[p])) { 16 | p = parent[p]; 17 | } 18 | if (cmp(arr[i], arr[p])) { 19 | parent[i] = parent[p], kids[i][0] = p; 20 | } else { 21 | parent[i] = p, kids[i][0] = kids[p][1]; 22 | } 23 | if (parent[i] != -1) { 24 | kids[parent[i]][1] = i; 25 | } 26 | if (kids[i][0] != -1) { 27 | parent[kids[i][0]] = i; 28 | } 29 | } 30 | 31 | int root = N - 1; 32 | while (parent[root] != -1) 33 | root = parent[root]; 34 | 35 | return make_tuple(root, move(parent), move(kids)); 36 | } 37 | -------------------------------------------------------------------------------- /code/struct/persistent_array.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | using namespace std; 5 | 6 | /** 7 | * Fat nodes implementation, partially persistent 8 | * 9 | * Complexity: O(1) set, O(log U) access where U is the number of writes on the index 10 | */ 11 | template 12 | struct persistent_fat_array { 13 | vector data; 14 | int N, V; 15 | vector>> fat; 16 | 17 | template 18 | explicit persistent_fat_array(Args&&... args) 19 | : data(forward(args)...), N(data.size()), V(1), fat(N) { 20 | for (int i = 0; i < N; i++) { 21 | fat[i].emplace_back(0, i); 22 | } 23 | } 24 | 25 | int num_nodes() const { return data.size(); } 26 | int versions() const { return V; } 27 | 28 | int set(int i, T element) { 29 | fat[i].emplace_back(V, num_nodes()); 30 | data.push_back(move(element)); 31 | return V++; 32 | } 33 | 34 | T& get(int v, int i) { 35 | auto at = upper_bound(begin(fat[i]), end(fat[i]), make_pair(v, num_nodes())); 36 | return data[prev(at)[1]]; 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /code/lib/lr.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "random.hpp" 4 | #include "numeric/polynomial.hpp" 5 | 6 | template // Lead and last terms guaranteed to be nonzero 7 | auto rand_recurrence(int n) { 8 | auto poly = polymath::rand_poly(n); 9 | for (int i : {0, n - 1}) { 10 | if (poly[i] == T()) { 11 | poly[i] = T(1); 12 | } 13 | } 14 | return poly; 15 | } 16 | 17 | template 18 | void extend_recurrence_inplace(int M, vector& x, const vector& rec) { 19 | x.resize(M); 20 | int N = rec.size(); 21 | for (int i = N; i < M; i++) { 22 | x[i] = 0; 23 | for (int j = 0; j < N; j++) { 24 | x[i] += rec[j] * x[i - j - 1]; 25 | } 26 | } 27 | } 28 | 29 | template 30 | bool verify_recurrence(const vector& x, const vector& rec) { 31 | int M = x.size(), N = rec.size(); 32 | for (int i = N; i < M; i++) { 33 | T want = 0; 34 | for (int j = 0; j < N; j++) { 35 | want += rec[j] * x[i - j - 1]; 36 | } 37 | if (want != x[i]) { 38 | return false; 39 | } 40 | } 41 | return true; 42 | } 43 | -------------------------------------------------------------------------------- /code/test/Makefile: -------------------------------------------------------------------------------- 1 | include ../../common/Makefile 2 | # DEBUG += $(SANIT) 3 | 4 | BIN_DIR := ./bin 5 | DEP_DIR := ./dep 6 | 7 | HEADERS := $(shell find .. -name '*.hpp' -wholename '*/*.hpp') 8 | CXX_FILES := $(wildcard *.cpp) 9 | CXX_FILES := $(filter-out $(FILE), $(CXX_FILES)) 10 | EXE_FILES := $(CXX_FILES:%.cpp=${BIN_DIR}/%) 11 | DEP_FILES := $(CXX_FILES:%.cpp=${DEP_DIR}/%.d) 12 | 13 | -include $(DEP_FILES) 14 | 15 | # global build 16 | 17 | CMD_MKDIRS = @mkdir -p $(BIN_DIR) $(DEP_DIR) 18 | CMD_MV_DEPENDENCY = @mv -f $(DEP_DIR)/$*.Td $(DEP_DIR)/$*.d && touch $@ 19 | 20 | DEPCOMPILEARGS = -MT $@ -MMD -MP -MF $(DEP_DIR)/$*.Td 21 | 22 | $(DEP_DIR)/%.d: ; 23 | .PRECIOUS: $(DEP_DIR)/%.d 24 | 25 | $(EXE_FILES): $(BIN_DIR)/%: %.cpp $(DEP_DIR)/%.d 26 | @echo "CC (${COMPILER} ${MODE}) $<" 27 | @$(CMD_MKDIRS) 28 | @$(CXX) $< $(CXXFLAGS) $(DEPCOMPILEARGS) -o $@ 29 | @$(CMD_MV_DEPENDENCY) 30 | 31 | .PHONY: all allperfm 32 | 33 | all: MODE := debug 34 | all: CXXFLAGS += $(DEBUG) 35 | all: $(EXE_FILES) 36 | 37 | allperfm: MODE := perfm 38 | allperfm: CXXFLAGS += $(OPTIM) 39 | allperfm: $(EXE_FILES) 40 | 41 | clean:: 42 | @rm -rf $(DEP_DIR) $(BIN_DIR) 43 | 44 | # --- 45 | 46 | ./solver: $(HEADERS) 47 | -------------------------------------------------------------------------------- /code/numeric/partitions.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "hash.hpp" 4 | #include "random.hpp" 5 | 6 | vector first_choice(int n, int k, int s) { 7 | vector comb(k); 8 | iota(begin(comb), end(comb), 1), (void)n; 9 | return comb; 10 | } 11 | 12 | bool next_choice(vector& comb, int n, int s) { 13 | for (int k = comb.size(), i = k - 1; i >= 0; i--) { 14 | if (comb[i] <= n - k + i + s) { 15 | comb[i]++; 16 | for (int j = i + 1; j < k; j++) { 17 | comb[j] = comb[j - 1] + 1; 18 | } 19 | return true; 20 | } 21 | } 22 | return false; 23 | } 24 | 25 | vector first_unsized_partition(int n) { return vector(n, 1); } 26 | 27 | bool next_unsized_partition(vector& a, bool self = true) { 28 | if (self && int(a.size()) == 1) { 29 | a.assign(a[0], 1); 30 | return false; 31 | } 32 | int k = a.size() - 1; 33 | int x = a[k - 1] + 1; 34 | int y = a[k] - 1; 35 | k -= 1; 36 | while (x <= y) { 37 | a.resize(k + 1); 38 | a[k] = x; 39 | y -= x; 40 | k += 1; 41 | } 42 | a.resize(k + 1); 43 | a[k] = x + y; 44 | return self || k > 0; 45 | } 46 | -------------------------------------------------------------------------------- /code/test/link_cut_tree_edge_path.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utils.hpp" 2 | #include "struct/link_cut_tree_path.hpp" 3 | #include "struct/link_cut_tree_edge_path.hpp" 4 | 5 | using lct_edge_path = link_cut_tree_edge_path; 6 | 7 | void unit_test_lct_edge_path() { 8 | const int N = 10; 9 | lct_edge_path lct(N); 10 | 11 | bool ok = true; 12 | ok &= lct.link(1, 2, 7); 13 | ok &= lct.link(2, 4, 2); 14 | ok &= lct.link(3, 7, 2); 15 | ok &= lct.link(1, 5, 11); 16 | ok &= lct.link(1, 3, 5); 17 | ok &= lct.link(8, 10, 6); 18 | ok &= lct.link(3, 8, 4); 19 | ok &= lct.link(3, 6, 8); 20 | ok &= lct.link(6, 9, 9); 21 | assert(ok); 22 | 23 | lct.reroot(2); 24 | cout << lct.lca(3, 5) << '\n'; // 1 25 | lct.reroot(3); 26 | cout << lct.lca(4, 5) << '\n'; // 1 27 | lct.reroot(7); 28 | cout << lct.lca(9, 4) << '\n'; // 3 29 | lct.reroot(2); 30 | cout << lct.lca(3, 5) << '\n'; // 1 31 | 32 | cout << lct.access_path(2, 7)->path << '\n'; // 7 33 | cout << lct.access_path(6, 5)->path << '\n'; // 11 34 | cout << lct.access_path(8, 9)->path << '\n'; // 9 35 | } 36 | 37 | int main() { 38 | RUN_SHORT(unit_test_lct_edge_path()); 39 | return 0; 40 | } 41 | -------------------------------------------------------------------------------- /code/struct/min_stack.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | using namespace std; 5 | 6 | template > 7 | struct min_stack { 8 | vector stack; 9 | Compare cmp; 10 | 11 | explicit min_stack(const Compare& cmp = Compare()) : cmp(cmp) {} 12 | 13 | auto pop() { 14 | T x = stack.back(); 15 | return stack.pop_back(), x; 16 | } 17 | void push(T x) { stack.emplace_back(empty() ? x : min(x, top(), cmp)); } 18 | const auto& top() const { return stack.back(); } 19 | int size() const { return stack.size(); } 20 | bool empty() const { return stack.empty(); } 21 | }; 22 | 23 | template 24 | struct window_stack { // Aggregates (older, newer) 25 | vector stack; 26 | BinOp binop; 27 | 28 | explicit window_stack(const BinOp& binop = BinOp()) : binop(binop) {} 29 | 30 | auto pop() { 31 | T x = stack.back(); 32 | return stack.pop_back(), x; 33 | } 34 | void push(T x) { stack.emplace_back(empty() ? x : binop(top(), x)); } 35 | const auto& top() const { return stack.back(); } 36 | int size() const { return stack.size(); } 37 | bool empty() const { return stack.empty(); } 38 | }; 39 | -------------------------------------------------------------------------------- /code/test/aho_corasick.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utils.hpp" 2 | #include "strings/aho_corasick.hpp" 3 | 4 | struct visitor { 5 | int cnt = 0; 6 | const vector& words; 7 | explicit visitor(const vector& words) : words(words) {} 8 | 9 | void operator()(int i, int wordid) { 10 | print("{:3}: index {:2} - {}\n", cnt++, i, words[wordid]); 11 | } 12 | }; 13 | 14 | void unit_test_aho_corasick() { 15 | vector words = { 16 | "aaab", "aab", "aaba", "aabb", "abc", "aca", "acb", "acba", 17 | "acbaa", "ba", "bba", "bbbbc", "bbbc", "bbbda", "bbc", "bc", 18 | "caa", "cadba", "cadbc", "cb", "cc", "ccaa", "ccb", 19 | }; 20 | string text = "aabababacbcabbaccadbcbacbabcabcababa"; 21 | aho_corasick aho(words); 22 | print("states: {}\n", aho.num_nodes()); 23 | print("matches: {}\n", aho.count_matches(text)); 24 | aho.visit_all(text, visitor(words)); 25 | print("unique matches: {}\n", aho.count_unique_matches(text)); 26 | aho.visit_longest_each_index(text, visitor(words)); 27 | auto v = aho.longest_each_index(text); 28 | cout << v << endl; 29 | } 30 | 31 | int main() { 32 | RUN_SHORT(unit_test_aho_corasick()); 33 | return 0; 34 | } 35 | -------------------------------------------------------------------------------- /code/algo/compress.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | using namespace std; 5 | 6 | // Compress vector u into type O with numbers 0,1,... handles ties. O(n log n) 7 | template 8 | auto compress(const vector& u, T s = 0) { 9 | int N = u.size(); 10 | if (N == 0) 11 | return vector(); 12 | 13 | vector> ps(N); 14 | for (int i = 0; i < N; i++) { 15 | ps[i] = {u[i], i}; 16 | } 17 | sort(begin(ps), end(ps)); 18 | 19 | vector v(N); 20 | v[ps[0].second] = s; 21 | for (int i = 1; i < N; i++) { 22 | v[ps[i].second] = v[ps[i - 1].second] + (ps[i].first != ps[i - 1].first); 23 | } 24 | return v; 25 | } 26 | 27 | template 28 | auto compress_string(const string& u) { 29 | int N = u.size(); 30 | if (N == 0) 31 | return vector(); 32 | 33 | vector> ps(N); 34 | for (int i = 0; i < N; i++) { 35 | ps[i] = {u[i], i}; 36 | } 37 | sort(begin(ps), end(ps)); 38 | 39 | vector v(N); 40 | v[ps[0].second] = 0; 41 | for (int i = 1; i < N; i++) { 42 | v[ps[i].second] = v[ps[i - 1].second] + (ps[i].first != ps[i - 1].first); 43 | } 44 | return v; 45 | } 46 | -------------------------------------------------------------------------------- /code/test/regular.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utils.hpp" 2 | #include "lib/graph_generator.hpp" 3 | 4 | void scaling_test_random_regular() { 5 | vector> table; 6 | table.push_back({"n", "k", "E", "time/edge", "time"}); 7 | 8 | auto run = [&](int n, int k) { 9 | printcl("scaling test random regular n,k={},{}", n, k); 10 | START(regular); 11 | LOOP_FOR_DURATION_TRACKED_RUNS (1s, now, runs) { 12 | auto g = random_regular(n, k); 13 | } 14 | TIME(regular); 15 | 16 | long E = 1L * runs * (n * k / 2); 17 | table.push_back({n, k, E, FORMAT_EACH(regular, E), FORMAT_EACH(regular, runs)}); 18 | }; 19 | 20 | run(300, 100); 21 | run(300, 200); 22 | run(25, 6); 23 | run(25, 10); 24 | run(36, 6); 25 | run(36, 8); 26 | run(36, 10); 27 | run(80, 5); 28 | run(80, 9); 29 | run(80, 13); 30 | run(400, 12); 31 | run(400, 16); 32 | run(400, 20); 33 | run(10000, 30); 34 | run(10000, 40); 35 | run(10000, 50); 36 | run(50000, 60); 37 | run(50000, 120); 38 | 39 | print_time_table(table, "Scaling random regular"); 40 | } 41 | 42 | int main() { 43 | RUN_BLOCK(scaling_test_random_regular()); 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /code/test/centroid_decomposition.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utils.hpp" 2 | #include "graphs/centroid_decomposition.hpp" 3 | #include "lib/graph_generator.hpp" 4 | #include "lib/graph_formats.hpp" 5 | 6 | void unit_test_centroid_decomposition() { 7 | int V = 38; 8 | string sedges = "0,1 0,2 0,3 0,4 1,5 1,6 2,7 2,8 3,9 3,10 6,11 6,36 6,37 7,28 7,29 " 9 | "8,12 8,32 8,33 9,14 9,15 9,20 10,13 10,16 10,17 11,34 11,35 12,30 " 10 | "12,31 13,18 13,19 14,24 14,25 14,26 14,27 15,21 15,23 21,22"; 11 | 12 | edges_t g = scan_edges(sedges); 13 | random_flip_graph_inplace(g); 14 | assert(int(g.size()) == V - 1); 15 | 16 | auto tree = make_adjacency_lists_undirected(V, g); 17 | auto [cparent, cdepth] = build_centroid_decomposition(tree); 18 | for (int u = 0; u < V; u++) { 19 | cout << (u ? " " : " ") << setw(2) << u << " \n"[u + 1 == V]; 20 | } 21 | for (int u = 0; u < V; u++) { 22 | cout << (u ? " " : "cparent ") << setw(2) << cparent[u] << " \n"[u + 1 == V]; 23 | } 24 | for (int u = 0; u < V; u++) { 25 | cout << (u ? " " : "cdepth ") << setw(2) << cdepth[u] << " \n"[u + 1 == V]; 26 | } 27 | } 28 | 29 | int main() { 30 | RUN_BLOCK(unit_test_centroid_decomposition()); 31 | return 0; 32 | } 33 | -------------------------------------------------------------------------------- /code/test/blackbox.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utils.hpp" 2 | #include "linear/blackbox/matrix.hpp" 3 | 4 | using num = modnum<998244353>; 5 | 6 | auto wolfram_circulant_matrix(vector v) { 7 | string s; 8 | for (int i = 0, n = v.size(); i < n; i++) { 9 | s += "(" + format("{}", fmt::join(v, ",")) + "),"; 10 | rotate(begin(v), end(v) - 1, end(v)); 11 | } 12 | s.pop_back(); 13 | return '(' + s + ')'; 14 | } 15 | 16 | void unit_test_blackbox() { 17 | outer_product_matrix M1({1, 2, 3}, {4, 5, 6}); 18 | vector x1 = {7, 8, 9}; 19 | putln(M1 * x1); 20 | 21 | circulant_matrix M2(8, {1, 2, 3, 4, 5, 6, 7, 8}); 22 | vector x2 = {1, -1, 2, -2, 3, -3, 4, -4}; 23 | putln(M2 * x2); 24 | putln(wolfram_circulant_matrix({1, 2, 3, 4, 5, 6, 7, 8})); 25 | putln(matrix_minimal_polynomial(M2)); 26 | putln(matrix_determinant(M2)); 27 | 28 | circulant_matrix M3(7, {1, 2, 3, 4, 5, 6, 7}); 29 | vector x3 = {1, -1, 2, -2, 3, -3, 4}; 30 | putln(M3 * x3); 31 | putln(wolfram_circulant_matrix({1, 2, 3, 4, 5, 6, 7})); 32 | putln(matrix_minimal_polynomial(M3)); 33 | putln(matrix_determinant(M3)); 34 | } 35 | 36 | int main() { 37 | RUN_BLOCK(unit_test_blackbox()); 38 | return 0; 39 | } 40 | -------------------------------------------------------------------------------- /code/geometry/duality2d.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "geometry/geometry2d.hpp" 4 | #include "numeric/quot.hpp" 5 | 6 | // Tools for exact computations with integer coordinates. 7 | // The implementation simulates the slope line moving from north to south in the plane 8 | struct Duality { 9 | // The dual of point P(a,b) is y=ax-b. The dual of line y=cx-d is P(c,d). 10 | using Stab = quot; 11 | static inline const Stab INF = Stab(+1, 0), NINF = Stab(-1, 0); 12 | 13 | // Abscissa of intersection of dual lines f and g 14 | static auto slope(Pt2 f, Pt2 g) { 15 | return f.x != g.x ? Stab(f.y - g.y, f.x - g.x) : INF; 16 | } 17 | 18 | // Compare dual lines f(x)=ax-b & g(x)=cx-d at vertical x. 19 | static bool compare(Pt2 f, Pt2 g, Stab x) { 20 | if (auto i = infsign(x)) { 21 | return i == +1 ? conj(f) < conj(g) : conj(f) > conj(g); 22 | } else if (auto dt = Pt2::L(f.x - g.x) * x.n - Pt2::L(f.y - g.y) * x.d) { 23 | return dt < 0; 24 | } else { 25 | return f.x < g.x; 26 | } 27 | } 28 | 29 | // Do dual lines f and g intersect at x? 30 | static bool equal(Pt2 f, Pt2 g, Stab x) { 31 | return x.d == 0 ? f.x == g.x : Pt2::L(f.x - g.x) * x.n == Pt2::L(f.y - g.y) * x.d; 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /code/test/topology.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utils.hpp" 2 | #include "graphs/topology.hpp" 3 | #include "lib/graph_operations.hpp" 4 | #include "lib/graph_formats.hpp" 5 | 6 | void unit_test_tree_centers() { 7 | using vi = vector; 8 | int V; 9 | vector> g; 10 | vector> tree; 11 | vector centers, diameter; 12 | 13 | V = 14; 14 | g = {{0, 2}, {1, 2}, {2, 5}, {3, 4}, {4, 5}, {11, 10}, {12, 10}, 15 | {13, 10}, {10, 8}, {8, 9}, {6, 9}, {9, 7}, {5, 9}}; 16 | tree = make_adjacency_lists_undirected(V, g); 17 | centers = find_tree_centers(tree); 18 | diameter = find_tree_diameter(tree); 19 | print("centers: {}\n", centers); 20 | print("diameter: {}\n", diameter); 21 | assert(centers.size() == 1 && centers[0] == 9); 22 | 23 | V = 11; 24 | g = {{0, 2}, {1, 2}, {2, 3}, {3, 4}, {3, 5}, {5, 6}, {5, 7}, {5, 8}, {8, 10}, {8, 9}}; 25 | tree = make_adjacency_lists_undirected(V, g); 26 | centers = find_tree_centers(tree); 27 | diameter = find_tree_diameter(tree); 28 | sort(begin(centers), end(centers)); 29 | print("centers: {}\n", centers); 30 | print("diameter: {}\n", diameter); 31 | assert(centers.size() == 2 && centers == vi({3, 5})); 32 | } 33 | 34 | int main() { 35 | RUN_SHORT(unit_test_tree_centers()); 36 | return 0; 37 | } 38 | -------------------------------------------------------------------------------- /code/test/voronoi.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utils.hpp" 2 | #include "geometry/voronoi.hpp" 3 | #include "geometry/generator2d.hpp" 4 | 5 | void speed_test_voronoi() { 6 | vector Ns = {6000, 15000, 30000, 50000, 100000, 200000, 300000, 500000, 800000}; 7 | 8 | vector> inputs; 9 | for (int N : Ns) { 10 | for (int i = 0; i < int(PointDistrib::END); i++) { 11 | inputs.push_back({N, PointDistrib(i)}); 12 | } 13 | } 14 | 15 | const auto runtime = 180000ms / inputs.size(); 16 | map, string> table; 17 | 18 | for (auto [N, dist] : inputs) { 19 | START_ACC(voronoi); 20 | 21 | LOOP_FOR_DURATION_TRACKED_RUNS (runtime, now, runs) { 22 | print_time(now, runtime, "speed voronoi {} N={}", N, to_string(dist)); 23 | 24 | auto pts = generate_points(N, dist, 0, 10'000'000); 25 | 26 | ADD_TIME_BLOCK(voronoi) { 27 | auto [F, hull, data, centers] = voronoi(pts); 28 | box_voronoi(pts, data[0], centers); 29 | Wedge::release(); 30 | } 31 | } 32 | 33 | table[{dist, N}] = FORMAT_EACH(voronoi, runs); 34 | } 35 | 36 | print_time_table(table, "Voronoi Diagram"); 37 | } 38 | 39 | int main() { 40 | RUN_BLOCK(speed_test_voronoi()); 41 | return 0; 42 | } 43 | -------------------------------------------------------------------------------- /code/geometry/algo2d.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "geometry/geometry2d.hpp" 4 | 5 | auto extract_points(const vector& indices, const vector& pts) { 6 | int N = indices.size(); 7 | vector ans(N); 8 | for (int i = 0; i < N; i++) { 9 | ans[i] = pts[indices[i]]; 10 | } 11 | return ans; 12 | } 13 | 14 | auto two_oriented_area(const vector& poly) { 15 | Pt2::L ans = 0; 16 | for (int i = 0, N = poly.size(); i < N; i++) { 17 | auto u = poly[i ? i - 1 : N - 1], v = poly[i]; 18 | ans += cross(u, v); 19 | } 20 | return ans; 21 | } 22 | auto two_area(const vector& poly) { return abs(two_oriented_area(poly)); } 23 | 24 | auto perimeter(const vector& poly) { 25 | double ans = 0; 26 | for (int i = 0, N = poly.size(); i < N; i++) { 27 | auto u = poly[i ? i - 1 : N - 1], v = poly[i]; 28 | ans += dist(u, v); 29 | } 30 | return ans; 31 | } 32 | 33 | auto centroid(const vector& poly) { 34 | array ans = {}; 35 | for (int i = 0, N = poly.size(); i < N; i++) { 36 | auto u = poly[i ? i - 1 : N - 1], v = poly[i]; 37 | ans[0] += double(u.x + v.x) * cross(u, v); 38 | ans[1] += double(u.y + v.y) * cross(u, v); 39 | } 40 | auto A = two_oriented_area(poly); 41 | ans[0] /= 6 * A, ans[1] /= 6 * A; 42 | return ans; 43 | } 44 | -------------------------------------------------------------------------------- /code/lib/2sat.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "random.hpp" 4 | 5 | using edges_t = vector>; 6 | 7 | // Generate a solvable 2sat problem with N variables, E edges. 8 | // Each variable in the canonical solution is true with probability true_p 9 | // Both variables in each edge are true with probability both_p 10 | edges_t generate_twosat(int N, int E, double true_p = 0.5, double both_p = 0.1) { 11 | intd distv(0, N - 1); 12 | vector hidden_solution(N); 13 | for (auto& v : hidden_solution) { 14 | v = cointoss(true_p); 15 | } 16 | edges_t g(E); 17 | for (auto& [u, v] : g) { 18 | u = rand_unif(0, N - 1); 19 | v = rand_unif(0, N - 1); 20 | u = hidden_solution[u] ? u : ~u; // 'left' node is true 21 | if (cointoss(both_p)) { 22 | v = hidden_solution[v] ? v : ~v; 23 | } else { 24 | v = hidden_solution[v] ? ~v : v; 25 | } 26 | if (cointoss(0.5)) { 27 | swap(u, v); 28 | } 29 | } 30 | return g; 31 | } 32 | 33 | bool verify_twosat(const edges_t& g, const vector& assignment) { 34 | return all_of(begin(g), end(g), [&](auto edge) { 35 | auto [u, v] = edge; 36 | return (u >= 0 && assignment[u]) || (u < 0 && !assignment[~u]) || 37 | (v >= 0 && assignment[v]) || (v < 0 && !assignment[~v]); 38 | }); 39 | } 40 | -------------------------------------------------------------------------------- /code/test/travelling_salesman.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utils.hpp" 2 | #include "graphs/travelling_salesman.hpp" 3 | #include "heuristic/tsp_ant_colony.hpp" 4 | 5 | void unit_test_stochastic_tsp() { 6 | int n = 1000, C = 2'000'000; 7 | 8 | // n=1000,repulsion=0: 90s,48s,35s 9 | // n=1000,repulsion=-1: 60s <4/<8 10 | // n=1000,repulsion=-3: 120s <6/<12, can still continue making progress 11 | 12 | vector> cost(n, vector(n)); 13 | long sum_all_costs = 0; 14 | 15 | for (int u = 0; u < n; u++) { 16 | for (int v = 0; v < n; v++) { 17 | if (u != v) { 18 | cost[u][v] = rand_wide(1, C, -1); 19 | sum_all_costs += cost[u][v]; 20 | } 21 | } 22 | } 23 | 24 | long opt_cost = 0; 25 | for (int i = 0; i < n; i++) { 26 | int u = i, v = (i + 1) % n; 27 | sum_all_costs -= cost[u][v]; 28 | cost[u][v] = rand_wide(1, C / n / 5, -3); 29 | sum_all_costs += cost[u][v]; 30 | opt_cost += cost[u][v]; 31 | } 32 | 33 | println("average cost: {}", sum_all_costs / (n - 1)); 34 | println(" opt cost: {}", opt_cost); 35 | 36 | auto [ants_cost, ants] = ant_colony_tour(cost, 5min); 37 | println("ants cost: {}", ants_cost); 38 | println("ants: {}", ants); 39 | } 40 | 41 | int main() { 42 | RUN_BLOCK(unit_test_stochastic_tsp()); 43 | return 0; 44 | } 45 | -------------------------------------------------------------------------------- /code/test/cartesian_tree.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utils.hpp" 2 | #include "algo/cartesian_tree.hpp" 3 | #include "algo/y_combinator.hpp" 4 | 5 | auto stress_test_cartesian_tree() { 6 | LOOP_FOR_DURATION_OR_RUNS_TRACKED (3s, now, 10000, runs) { 7 | print_time(now, 3s, "stress cartesian tree ({} runs)", runs); 8 | 9 | int N = rand_unif(1, 1000); 10 | vector index(N); 11 | iota(begin(index), end(index), 0); 12 | vector arr = index; 13 | shuffle(begin(arr), end(arr), mt); 14 | 15 | auto [root, parent, kids] = cartesian_tree(arr); 16 | vector inorder; 17 | 18 | y_combinator([&, kids = kids, parent = parent](auto self, int i) -> void { 19 | if (kids[i][0] != -1) { 20 | self(kids[i][0]); 21 | assert(kids[i][0] < i); 22 | assert(parent[kids[i][0]] == i); 23 | assert(arr[i] < arr[kids[i][0]]); 24 | } 25 | inorder.push_back(i); 26 | if (kids[i][1] != -1) { 27 | self(kids[i][1]); 28 | assert(i < kids[i][1]); 29 | assert(parent[kids[i][1]] == i); 30 | assert(arr[i] < arr[kids[i][1]]); 31 | } 32 | })(root); 33 | 34 | assert(inorder == index); 35 | } 36 | } 37 | 38 | int main() { 39 | RUN_BLOCK(stress_test_cartesian_tree()); 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /code/test/2sat.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utils.hpp" 2 | #include "algo/2sat.hpp" 3 | #include "lib/2sat.hpp" 4 | 5 | void speed_test_twosat_positive() { 6 | vector Ns = {6000, 15000, 30000, 50000, 100000, 200000, 300000, 500000, 800000}; 7 | vector Es = {2, 5, 10, 20, 35}; 8 | 9 | vector> inputs; 10 | for (int N : Ns) { 11 | for (int E : Es) { 12 | if (N * E <= 5'000'000) { 13 | inputs.push_back({N, E}); 14 | } 15 | } 16 | } 17 | const auto runtime = 90'000ms / inputs.size(); 18 | map, string> table; 19 | 20 | for (auto [N, E] : inputs) { 21 | START_ACC(sat); 22 | 23 | LOOP_FOR_DURATION_OR_RUNS_TRACKED (runtime, now, 1000, runs) { 24 | print_time(now, runtime, "speed 2-sat N,E={},{}", N, N * E); 25 | 26 | auto g = generate_twosat(N, N * E); 27 | 28 | START(sat); 29 | twosat_scc sat(N); 30 | for (auto [u, v] : g) { 31 | sat.either(u, v); 32 | } 33 | bool ok = sat.solve(); 34 | ADD_TIME(sat); 35 | 36 | assert(ok && verify_twosat(g, sat.assignment)); 37 | } 38 | 39 | table[{E, N}] = FORMAT_EACH(sat, runs); 40 | } 41 | 42 | print_time_table(table, "2SAT"); 43 | } 44 | 45 | int main() { 46 | RUN_BLOCK(speed_test_twosat_positive()); 47 | return 0; 48 | } 49 | -------------------------------------------------------------------------------- /code/test/dominator_tree.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utils.hpp" 2 | #include "lib/graph_formats.hpp" 3 | #include "graphs/dominator_tree.hpp" 4 | 5 | void unit_test_dominator_tree() { 6 | edges_t g; 7 | int V; 8 | enum nodenames { _, R = 1, A, B, C, D, E, F, G, H, I = 10, J, K, L, M, N, O, P, Q }; 9 | char name[] = "_RABCDEFGHIJKLMNOPQ"; 10 | 11 | auto run = [&]() { 12 | auto out = make_adjacency_lists_directed(V, g); 13 | auto [dom, parent] = build_dominator_tree(R, out); 14 | for (int u = 1; u < V; u++) { 15 | print("dom[{}] = {} parent[{}] = {}\n", name[u], name[dom[u]], name[u], 16 | name[parent[u]]); 17 | } 18 | print("---\n"); 19 | }; 20 | 21 | V = 14; 22 | g = { 23 | {R, A}, {R, B}, {R, C}, {A, D}, {B, A}, {B, D}, {B, E}, 24 | {C, F}, {C, G}, {D, L}, {E, H}, {F, I}, {G, I}, {G, J}, 25 | {H, E}, {H, K}, {I, K}, {J, I}, {K, I}, {K, R}, {L, H}, 26 | }; 27 | run(); 28 | 29 | V = 18; 30 | g = { 31 | {R, A}, {R, I}, {A, B}, {A, C}, {A, H}, {I, B}, {J, A}, {B, C}, 32 | {H, J}, {H, C}, {C, D}, {C, K}, {C, I}, {K, L}, {D, M}, {D, E}, 33 | {D, H}, {L, D}, {L, K}, {L, E}, {L, P}, {M, D}, {M, N}, {E, F}, 34 | {N, E}, {N, O}, {F, N}, {F, L}, {F, P}, {F, G}, {P, G}, {G, O}, 35 | }; 36 | run(); 37 | } 38 | 39 | int main() { 40 | RUN_SHORT(unit_test_dominator_tree()); 41 | return 0; 42 | } 43 | -------------------------------------------------------------------------------- /common/Makefile: -------------------------------------------------------------------------------- 1 | # C++ template (COMPILER=(gcc or clang), CC=(executable name/path, e.g. g++ or clang++)) 2 | COMPILER := gcc 3 | CC := g++ 4 | CPP_STANDARD := c++17 5 | EXTRA_CXXFLAGS := -DLOCAL 6 | LIBS := -lfmt 7 | 8 | export GIT_ROOT := $(shell run get-root) 9 | export CXX := $(CC) -std=$(CPP_STANDARD) -pipe -pthread -pedantic -march=native 10 | 11 | include $(GIT_ROOT)/common/cxxwarns.make 12 | GCH_ROOT := $(GIT_ROOT)/.precompiled-headers 13 | 14 | export USE_CLANG_LIBCPP := -stdlib=libc++ 15 | export USE_GLIBS_DEBUG := -D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC 16 | export USE_GCH := -I$(GCH_ROOT) 17 | 18 | export INCLUDES := -I$(GIT_ROOT)/code 19 | export CXXFLAGS := $(WARNS) $(CXXFLAGS) $(EXTRA_CXXFLAGS) $(LIBS) $(INCLUDES) 20 | export OPTIM := -g -O3 -flto -funroll-loops -ftree-vectorize 21 | export DEBUG := -g -Og -ggdb $(USE_GLIBS_DEBUG) 22 | 23 | .PHONY: debug perfm clean 24 | 25 | debug: MODE := debug 26 | debug: CXXFLAGS += $(DEBUG) 27 | debug: ./solver 28 | 29 | perfm: MODE := perfm 30 | perfm: CXXFLAGS += $(OPTIM) 31 | perfm: ./solver 32 | 33 | clean:: 34 | @rm -f solver hacker judger core vgcore.* *.ans *.log output.txt answer.txt 35 | 36 | ./solver: code.cpp 37 | @echo CC ${COMPILER} ${MODE} $@ 38 | @$(CXX) code.cpp $(CXXFLAGS) -o $@ 39 | 40 | ./hacker: hack.cpp 41 | @echo CC ${COMPILER} $@ 42 | @$(CXX) hack.cpp $(CXXFLAGS) $(OPTIM) -o $@ 43 | 44 | ./judger: judge.cpp 45 | @echo CC ${COMPILER} $@ 46 | @$(CXX) judge.cpp $(CXXFLAGS) $(OPTIM) -o $@ 47 | -------------------------------------------------------------------------------- /code/algo/floor_sum.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | using namespace std; 5 | 6 | uint64_t floor_sum_unsigned(uint64_t n, uint64_t m, uint64_t a, uint64_t b) { 7 | assert(n < (1LL << 32) && 1 <= m && m < (1LL << 32)); 8 | uint64_t ans = 0; 9 | while (true) { 10 | if (a >= m) { 11 | ans += n * (n - 1) / 2 * (a / m); 12 | a %= m; 13 | } 14 | if (b >= m) { 15 | ans += n * (b / m); 16 | b %= m; 17 | } 18 | 19 | uint64_t y_max = a * n + b; 20 | if (y_max < m) { 21 | break; 22 | } 23 | 24 | n = y_max / m; 25 | b = y_max % m; 26 | swap(a, m); 27 | } 28 | return ans; 29 | } 30 | 31 | // n-1 | a*i+b | 32 | // Compute SUM | ----- | in O(log²m) 33 | // i=0 |_ m _| 34 | // Notice it includes the case i=0 and is not inclusive for n. 35 | int64_t floor_sum(uint64_t n, uint64_t m, int64_t a, int64_t b) { 36 | assert(n < (1LL << 32) && 1 <= m && m < (1LL << 32)); 37 | uint64_t ans = 0; 38 | if (a < 0) { 39 | int64_t a2 = a % m; 40 | a2 = a2 < 0 ? a2 + m : a; 41 | ans -= 1ULL * n * (n - 1) / 2 * ((a2 - a) / m); 42 | a = a2; 43 | } 44 | if (b < 0) { 45 | int64_t b2 = b % m; 46 | b2 = b2 < 0 ? b2 + m : b; 47 | ans -= 1ULL * n * ((b2 - b) / m); 48 | b = b2; 49 | } 50 | return ans + floor_sum_unsigned(n, m, a, b); 51 | } 52 | -------------------------------------------------------------------------------- /code/linear/berlekamp_massey.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "numeric/polynomial.hpp" 4 | 5 | // Compute the smallest linear recurrence given in rec[0..n) by 6 | // x[n+k] = rec[0]x[n+k-1] + rec[1]x[n+k-2] + ... + rec[n-1]x[k], for all k>=0 7 | // that produces the given output x. The output should be larger than 2n. O(n|x|) 8 | template 9 | vector berlekamp_massey(const vector& x) { 10 | const int M = x.size(); 11 | vector b, c; 12 | if (M == 0) { 13 | return c; 14 | } 15 | 16 | b.reserve(M + 1), c.reserve(M + 1); 17 | b.push_back(-1), c.push_back(-1); 18 | T lead = 1; 19 | 20 | for (int i = 0; i < M; i++) { 21 | int N = c.size(), m = b.size(); 22 | T delta = 0; 23 | for (int j = 0; j < N; j++) { 24 | delta -= c[j] * x[i + j - N + 1]; 25 | } 26 | b.push_back(0), m++; 27 | if (delta == T()) { 28 | continue; 29 | } 30 | T fix = delta / lead; 31 | if (N < m) { 32 | auto a = c; 33 | c.insert(begin(c), m - N, T(0)); 34 | for (int j = 0; j < m; j++) { 35 | c[m - 1 - j] -= fix * b[m - 1 - j]; 36 | } 37 | b = move(a); 38 | lead = delta; 39 | } else { 40 | for (int j = 0; j < m; j++) { 41 | c[N - 1 - j] -= fix * b[m - 1 - j]; 42 | } 43 | } 44 | } 45 | 46 | c.pop_back(), reverse(begin(c), end(c)); 47 | return c; 48 | } 49 | -------------------------------------------------------------------------------- /code/test/runge_kutta.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utils.hpp" 2 | #include "numeric/runge_kutta.hpp" 3 | 4 | double sine(double x) { return sin(x); } 5 | double cosine(double x) { return cos(x); } 6 | 7 | void unit_test_simpson() { 8 | for (int n = 1; n <= 20; n++) { 9 | println("simpson(N={}): {:2.15f}", 1 << n, simpson(1 << n, 0.0, 1.0, sine)); 10 | } 11 | } 12 | 13 | void unit_test_runge_kutta_explicit() { 14 | auto f = [](double, double, double z) { return z; }; 15 | auto g = [](double, double y, double z) { return (1 - y * y) * z - y; }; 16 | 17 | int N = 10000, S = 50; 18 | auto trace1 = explicit_rk4_two(N, 0.0, 1.0, 0.0, 25.0, f, g); 19 | println("--- Explicit van der pol"); 20 | for (int i = 0; i < S; i++) { 21 | auto [x, y, z] = trace1[N / S * i]; 22 | println("t:{:7.3f} y:{:7.3f} z:{:7.3f}", x, y, z); 23 | } 24 | } 25 | 26 | void unit_test_runge_kutta_implicit() { 27 | auto f = [](double x, double y, double z) { return 2 * cos(z) - sin(y) - x; }; 28 | 29 | int N = 10000, S = 50; 30 | auto trace1 = implicit_rk1_one(N, 20, 0.0, 1.0, 1.0, 0.5, 1e-9, f); 31 | println("--- Implicit trig"); 32 | for (int i = 0; i < S; i++) { 33 | auto [x, y, z] = trace1[N / S * i]; 34 | println("t:{:7.3f} y:{:7.3f} z:{:7.3f} f: {}", x, y, z, f(x, y, z)); 35 | } 36 | } 37 | 38 | int main() { 39 | RUN_BLOCK(unit_test_simpson()); 40 | RUN_BLOCK(unit_test_runge_kutta_explicit()); 41 | RUN_BLOCK(unit_test_runge_kutta_implicit()); 42 | return 0; 43 | } 44 | -------------------------------------------------------------------------------- /code/test/archive/basic_bs_tree.cpp: -------------------------------------------------------------------------------- 1 | #undef NDEBUG 2 | #include "test_utils.hpp" 3 | #include "../bstree/bs_set.hpp" 4 | #include "../bstree/bs_tree_debug.hpp" 5 | 6 | using namespace std; 7 | 8 | /** 9 | * Simplified version of bs_tree.cpp test suite with just the basic insert and erase 10 | * operations, to test new or buggy cores (splay) 11 | */ 12 | 13 | template struct bs_set; 14 | 15 | void insert_test() { 16 | intd distn(1, 999); 17 | intd dists(0, 3000); 18 | 19 | LOOP_FOR_DURATION_TRACKED (3s, now) { 20 | print_time(now, 3s, "insert test"); 21 | bs_set tree; 22 | for (int i = 0, s = dists(mt); i < s; i++) { 23 | tree.insert(distn(mt)); 24 | } 25 | debug_tree(tree).debug(); 26 | } 27 | } 28 | 29 | void erase_test() { 30 | intd distn(1, 999); 31 | intd dists(0, 3000); 32 | 33 | LOOP_FOR_DURATION_TRACKED (3s, now) { 34 | print_time(now, 3s, "erase test"); 35 | bs_set tree; 36 | vector nums; 37 | int s = dists(mt); 38 | for (int i = 0; i < s; i++) { 39 | int n = distn(mt); 40 | nums.push_back(n); 41 | tree.insert(n); 42 | } 43 | debug_tree(tree).debug(); 44 | shuffle(begin(nums), end(nums), mt); 45 | for (int i = 0; i < s; i++) { 46 | tree.erase(nums[i]); 47 | debug_tree(tree).debug(); 48 | } 49 | assert(tree.empty()); 50 | } 51 | } 52 | 53 | int main() { 54 | RUN_SHORT(insert_test()); 55 | RUN_SHORT(erase_test()); 56 | return 0; 57 | } 58 | -------------------------------------------------------------------------------- /code/test/mergesort_tree.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utils.hpp" 2 | #include "struct/mergesort_tree.hpp" 3 | 4 | void stress_test_mergesort_tree() { 5 | LOOP_FOR_DURATION_OR_RUNS_TRACKED (20s, now, 10'000, runs) { 6 | print_time(now, 20s, "stress wavelet tree ({} runs)", runs); 7 | int N = rand_unif(2, 150); 8 | int MIN = rand_unif(-50, -1); 9 | int MAX = rand_unif(1, 50); 10 | 11 | vector arr = rands_grav(N, MIN, MAX, 2); 12 | mergesort_tree mst(arr); 13 | 14 | for (int l = 0; l <= N; l++) { 15 | for (int r = l; r <= N; r++) { 16 | for (int x : rands_unif(4, MIN, MAX)) { 17 | int less = 0; 18 | int less_equal = 0; 19 | int greater = 0; 20 | int greater_equal = 0; 21 | 22 | for (int i = l; i < r; i++) { 23 | less += arr[i] < x; 24 | less_equal += arr[i] <= x; 25 | greater += arr[i] > x; 26 | greater_equal += arr[i] >= x; 27 | } 28 | 29 | assert(less == mst.count_less(l, r, x)); 30 | assert(less_equal == mst.count_equal_or_less(l, r, x)); 31 | assert(greater == mst.count_greater(l, r, x)); 32 | assert(greater_equal == mst.count_equal_or_greater(l, r, x)); 33 | } 34 | } 35 | } 36 | } 37 | } 38 | 39 | int main() { 40 | RUN_BLOCK(stress_test_mergesort_tree()); 41 | return 0; 42 | } 43 | -------------------------------------------------------------------------------- /code/strings/z_search.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | using namespace std; 5 | 6 | /** 7 | * Z algorithm (prefix length array) 8 | * https://www.hackerearth.com/practice/algorithms/string-algorithm/z-algorithm/tutorial 9 | */ 10 | int z_search(const string& text, const string& needle, char null = '\0') { 11 | string s = needle + null + text; 12 | int P = needle.size(), S = s.size(); 13 | vector z(S); 14 | int L = 0, R = 0; 15 | for (int i = 1; i < S; i++) { 16 | if (i < R && z[i - L] < R - i) { 17 | z[i] = z[i - L]; 18 | } else { 19 | L = i; 20 | R = max(R, i); 21 | while (R < S && s[R - L] == s[R]) { 22 | R++; 23 | } 24 | z[i] = R - L; 25 | } 26 | if (i > P && z[i] == P) { 27 | return i - (P + 1); 28 | } 29 | } 30 | return -1; 31 | } 32 | 33 | vector z_search_all(const string& text, const string& needle, char null = '\0') { 34 | string s = needle + null + text; 35 | int P = needle.size(), S = s.size(); 36 | vector z(S); 37 | int L = 0, R = 0; 38 | vector match; 39 | 40 | for (int i = 1; i < S; i++) { 41 | if (i < R && z[i - L] < R - i) { 42 | z[i] = z[i - L]; 43 | } else { 44 | L = i; 45 | R = max(R, i); 46 | while (R < S && s[R - L] == s[R]) { 47 | R++; 48 | } 49 | z[i] = R - L; 50 | } 51 | if (i > P && z[i] == P) { 52 | match.push_back(i - (P + 1)); 53 | } 54 | } 55 | return match; 56 | } 57 | -------------------------------------------------------------------------------- /code/struct/pbds.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | using namespace std; 5 | 6 | // Order statistics tree/map 7 | 8 | #include 9 | #include 10 | namespace gnu = __gnu_pbds; 11 | 12 | template > 13 | using ordered_map = gnu::tree; 15 | 16 | template > 17 | using ordered_set = ordered_map; 18 | 19 | // Fast min priority queue 20 | 21 | #include 22 | namespace gnu = __gnu_pbds; 23 | 24 | template > 25 | using max_heap = gnu::priority_queue; 26 | // using min_heap_t = max_heap, greater<>>; 27 | // using iter_t = heap_t::point_const_iterator; 28 | 29 | // Fast hashset/map 30 | 31 | #include 32 | #include 33 | namespace gnu = __gnu_pbds; 34 | 35 | template > 36 | using hash_map = gnu::gp_hash_table; 37 | 38 | template > 39 | using hash_set = hash_map; 40 | 41 | namespace __gnu_pbds { 42 | 43 | template 44 | string to_string(const ordered_set& os) { 45 | return seq_to_string(os); 46 | } 47 | 48 | template 49 | ostream& operator<<(ostream& out, const ordered_set& os) { 50 | return out << to_string(os); 51 | } 52 | 53 | } // namespace __gnu_pbds 54 | -------------------------------------------------------------------------------- /code/graphs/heavy_light_decomposition.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "algo/y_combinator.hpp" 4 | 5 | // Standard HLD decomposition of tree/forest. Moves the heavy child to tree[u][0]. O(n) 6 | auto build_heavy_light_decomposition(vector>& tree, int root = 0) { 7 | int V = tree.size(); 8 | vector subsize(V), heavy(V, -1), parent(V, -1), depth(V); 9 | vector head(V), tin(V), tout(V); 10 | int timer = 0; 11 | 12 | auto dfs = y_combinator([&](auto self, int u, int p) -> void { 13 | subsize[u] = 1; 14 | int largest = 0; 15 | for (int& v : tree[u]) { 16 | if (v != p) { 17 | parent[v] = u; 18 | depth[v] = depth[u] + 1; 19 | self(v, u); 20 | subsize[u] += subsize[v]; 21 | if (largest < subsize[v]) { 22 | largest = subsize[v]; 23 | heavy[u] = v; 24 | swap(tree[u][0], v); 25 | } 26 | } 27 | } 28 | }); 29 | 30 | auto decompose = y_combinator([&](auto self, int u, int h, int p) -> void { 31 | head[u] = h; 32 | tin[u] = timer++; 33 | for (int v : tree[u]) { 34 | if (v != p) { 35 | self(v, v == heavy[u] ? h : v, u); 36 | } 37 | } 38 | tout[u] = timer; 39 | }); 40 | 41 | dfs(root, -1); 42 | decompose(root, root, -1); 43 | 44 | for (int u = 0; u < V; u++) { 45 | if (subsize[u] == 0) { 46 | dfs(u, -1); 47 | decompose(u, u, -1); 48 | } 49 | } 50 | 51 | return make_tuple(move(parent), move(depth), move(head), move(tin), move(tout)); 52 | } 53 | -------------------------------------------------------------------------------- /code/test/wavelet_tree.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utils.hpp" 2 | #include "struct/wavelet_tree.hpp" 3 | 4 | void stress_test_wavelet_tree() { 5 | mt.seed(73); 6 | LOOP_FOR_DURATION_OR_RUNS_TRACKED (20s, now, 10'000, runs) { 7 | print_time(now, 20s, "stress wavelet tree ({} runs)", runs); 8 | int N = rand_unif(2, 30); 9 | int MIN = 0; 10 | int MAX = rand_unif(1, 50); 11 | 12 | vector arr = rands_grav(N, MIN, MAX - 1, 2); 13 | wavelet_tree wvl(MIN, MAX, arr); 14 | 15 | for (int l = 0; l <= N; l++) { 16 | for (int r = l; r <= N; r++) { 17 | map cnt; 18 | for (int i = l; i < r; i++) { 19 | cnt[arr[i]]++; 20 | } 21 | 22 | vector brr(begin(arr) + l, begin(arr) + r); 23 | sort(begin(brr), end(brr)); 24 | 25 | for (int x = MIN + 1; x < MAX; x++) { 26 | cnt[x] += cnt[x - 1]; 27 | } 28 | 29 | for (int k = 0; k < r - l; k++) { 30 | assert(wvl.find_by_order(l, r, k) == brr[k]); 31 | } 32 | 33 | for (int x = MIN; x <= MAX; x++) { 34 | for (int y = x; y <= MAX; y++) { 35 | int c = x < y ? cnt[y - 1] - cnt[x - 1] : 0; 36 | int d = wvl.count_within(l, r, x, y); 37 | if (c != d) { 38 | debug(l, r, MIN, MAX, x, y, c, d); 39 | } 40 | assert(c == d); 41 | } 42 | } 43 | } 44 | } 45 | } 46 | } 47 | 48 | int main() { 49 | RUN_BLOCK(stress_test_wavelet_tree()); 50 | return 0; 51 | } 52 | -------------------------------------------------------------------------------- /code/numeric/int128.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "hash.hpp" 4 | 5 | using int128_t = __int128_t; 6 | 7 | int128_t gcd(int128_t a, int128_t b) { return b == 0 ? a : gcd(b, a % b); } 8 | int128_t lcm(int128_t a, int128_t b) { return (a / gcd(a, b)) * b; } 9 | 10 | int128_t to_int128(const string& s) { 11 | int128_t val = 0; 12 | int S = s.size(); 13 | for (int i = 0; i < S; i++) { 14 | if ('0' <= s[i] && s[i] <= '9') { 15 | val = 10 * val + (s[i] - '0'); 16 | } else { 17 | break; 18 | } 19 | } 20 | return val; 21 | } 22 | 23 | namespace std { 24 | string to_string(int128_t x) { 25 | if (x == 0) 26 | return "0"; 27 | __uint128_t k = x; 28 | if (k == (__uint128_t(1) << 127)) 29 | return "-170141183460469231731687303715884105728"; 30 | string result; 31 | if (x < 0) { 32 | result += "-"; 33 | x *= -1; 34 | } 35 | string t; 36 | while (x) { 37 | t.push_back('0' + x % 10); 38 | x /= 10; 39 | } 40 | reverse(t.begin(), t.end()); 41 | return result + t; 42 | } 43 | 44 | ostream& operator<<(ostream& o, int128_t x) { return o << to_string(x); } 45 | istream& operator>>(istream& o, int128_t& x) { 46 | string s; 47 | return o >> s, x = to_int128(s), o; 48 | } 49 | } // namespace std 50 | 51 | struct Int128Hasher { 52 | size_t operator()(__uint128_t x) const noexcept { 53 | array* arr = reinterpret_cast*>(&x); 54 | return Hasher{}(*arr); 55 | } 56 | size_t operator()(int128_t x) const noexcept { 57 | array* arr = reinterpret_cast*>(&x); 58 | return Hasher{}(*arr); 59 | } 60 | }; 61 | 62 | namespace std { 63 | 64 | template <> 65 | struct hash : Int128Hasher {}; 66 | template <> 67 | struct hash<__uint128_t> : Int128Hasher {}; 68 | 69 | } // namespace std 70 | -------------------------------------------------------------------------------- /code/test/ntt.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utils.hpp" 2 | #include "numeric/fft.hpp" 3 | 4 | using num = modnum<998244353>; 5 | 6 | void stress_test_ntt_multiply() { 7 | LOOP_FOR_DURATION_OR_RUNS_TRACKED (20s, now, 100'000, runs) { 8 | print_time(now, 20s, "stress ntt ({} runs)", runs); 9 | 10 | const int V = 100'000; 11 | int A = rand_unif(0, 1000); 12 | int B = rand_unif(0, 1000); 13 | auto a = rands_unif(A, -V, V); 14 | auto b = rands_unif(B, -V, V); 15 | auto c = fft::ntt_multiply(a, b); 16 | auto d = fft::naive_multiply(a, b); 17 | 18 | assert(c == d); 19 | } 20 | } 21 | 22 | void speed_test_ntt_multiply() { 23 | const int V = 1'000'000; 24 | vector As = {6000, 15000, 30000, 50000, 100000, 200000, 300000, 500000, 800000}; 25 | vector Bs = {6000, 15000, 30000, 50000, 100000, 200000, 300000, 500000, 800000}; 26 | 27 | vector> inputs; 28 | for (int A : As) { 29 | for (int B : Bs) { 30 | inputs.push_back({A, B}); 31 | } 32 | } 33 | 34 | const auto runtime = 120'000ms / (As.size() * Bs.size()); 35 | map, string> table; 36 | 37 | for (auto [A, B] : inputs) { 38 | START_ACC(ntt); 39 | 40 | LOOP_FOR_DURATION_OR_RUNS_TRACKED (runtime, now, 10'000, runs) { 41 | print_time(now, runtime, "speed ntt A={} B={} ({} runs)", A, B, runs); 42 | 43 | auto a = rands_unif(A, -V, V); 44 | auto b = rands_unif(B, -V, V); 45 | 46 | ADD_TIME_BLOCK(ntt) { fft::ntt_multiply(a, b); } 47 | } 48 | 49 | table[{A, B}] = FORMAT_EACH(ntt, runs); 50 | } 51 | 52 | print_time_table(table, "NTT multiply"); 53 | } 54 | 55 | int main() { 56 | RUN_BLOCK(stress_test_ntt_multiply()); 57 | RUN_BLOCK(speed_test_ntt_multiply()); 58 | return 0; 59 | } 60 | -------------------------------------------------------------------------------- /code/graphs/travelling_salesman.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "numeric/bits.hpp" // FOR_EACH_MASK, FOR_EACH_BIT 4 | 5 | /** 6 | * Held-Karp dynamic programming exact algorithm 7 | * Complexity: O(V^2 2^V) (~1s for V=22) 8 | * Memory: O(V 2^V) (prohibitive for V>25) 9 | */ 10 | long tsp_held_karp(int V, const vector>& dist, vector* out_path) { 11 | static constexpr int inf = INT_MAX / 2; 12 | int n = V - 1; 13 | vector> cost(1 << n, vector(n, inf)); 14 | for (int i = 0; i < n; i++) { 15 | cost[1 << i][i] = dist[n][i]; 16 | } 17 | for (int s = 2; s <= n; s++) { 18 | FOR_EACH_MASK (set, s, n) { 19 | FOR_EACH_BIT_NUMBER (kbit, k, set) { 20 | FOR_EACH_BIT_NUMBER (mbit, m, set ^ kbit) { 21 | cost[set][k] = min(cost[set][k], cost[set ^ kbit][m] + dist[m][k]); 22 | } 23 | } 24 | } 25 | } 26 | // find the optimum and recover the path 27 | int optimum = inf; 28 | int set = (1 << n) - 1, k = n; 29 | for (int i = 0; i < n; i++) { 30 | if (optimum > cost[set][i] + dist[i][n]) { 31 | optimum = cost[set][i] + dist[i][n]; 32 | k = i; 33 | } 34 | } 35 | if (out_path == nullptr) 36 | return optimum; 37 | vector path; 38 | path.push_back(k); 39 | for (int s = n; s >= 2; s--) { 40 | int kbit = 1 << k; 41 | FOR_EACH_BIT_NUMBER (mbit, m, set ^ kbit) { 42 | if (cost[set][k] == cost[set ^ kbit][m] + dist[m][k]) { 43 | path.push_back(m); 44 | set ^= kbit; 45 | k = m; 46 | break; 47 | } 48 | } 49 | } 50 | path.push_back(n); 51 | reverse(begin(path), end(path)); 52 | rotate(begin(path), find(begin(path), end(path), 0), end(path)); 53 | *out_path = path; 54 | return optimum; 55 | } 56 | -------------------------------------------------------------------------------- /code/algo/hornsat.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | using namespace std; 5 | 6 | // Solve horn-sat and compute assignment. O(n) 7 | // Usage: 8 | // vector> clauses; 9 | // ... first variable in clause is positive, rest is negated ... 10 | // vector assignment; 11 | // bool satisfiable = hornsat(clauses, &assignment); 12 | bool hornsat(vector> clauses, vector* assignment = nullptr) { 13 | int N = 0, C = clauses.size(); 14 | for (int c = 0; c < C; c++) { 15 | int S = clauses[c].size(); 16 | assert(S >= 1); 17 | for (int i = 0; i < S; i++) { 18 | int var = clauses[c][i]; 19 | N = max(N, var + 1); 20 | assert((i == 0 && var == -1) || var >= 0); 21 | } 22 | if (S == 1 && clauses[c][0] == -1) { 23 | return false; 24 | } 25 | } 26 | 27 | vector units; 28 | vector unvisited(C); 29 | vector> in_tail(N); 30 | vector solution(N); 31 | 32 | for (int c = 0; c < C; c++) { 33 | int S = clauses[c].size(); 34 | if (S == 1) { 35 | units.push_back(c); 36 | } 37 | unvisited[c] = S - 1; 38 | for (int i = 1; i < S; i++) { 39 | in_tail[clauses[c][i]].push_back(i); 40 | } 41 | } 42 | 43 | while (!units.empty()) { 44 | int c = units.back(); 45 | units.pop_back(); 46 | int head = clauses[c][0]; 47 | if (head == -1) { 48 | return false; 49 | } 50 | if (solution[head]) { 51 | continue; 52 | } 53 | solution[head] = true; 54 | for (int j : in_tail[head]) { 55 | if (--unvisited[j] == 0) { 56 | units.push_back(j); 57 | } 58 | } 59 | } 60 | 61 | if (assignment) { 62 | *assignment = move(solution); 63 | } 64 | return true; 65 | } 66 | -------------------------------------------------------------------------------- /code/test/mincost_matching.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utils.hpp" 2 | #include "lib/bipartite_matching.hpp" 3 | #include "matching/fast_dense_hungarian.hpp" 4 | 5 | void stress_test_mincost_matching() { 6 | LOOP_FOR_DURATION_OR_RUNS_TRACKED (20s, now, 50000, runs) { 7 | print_time(now, 20s, "stress mincost matching ({} runs)", runs); 8 | 9 | int V = rand_unif(1, 200); 10 | 11 | vector> cost(V, vector(V)); 12 | for (int u = 0; u < V; u++) { 13 | for (int v = 0; v < V; v++) { 14 | cost[u][v] = u == v ? 0 : rand_unif(1, 50'000'000); 15 | } 16 | } 17 | 18 | fast_dense_hungarian(cost); 19 | 20 | // TODO: augmenting path verification 21 | } 22 | } 23 | 24 | void speed_test_mincost_matching() { 25 | vector Vs = {60, 150, 300, 500, 1000, 2000, 3000, 5000, 8000}; 26 | 27 | vector inputs = Vs; 28 | 29 | const auto runtime = 120'000ms / inputs.size(); 30 | map, stringable> table; 31 | 32 | for (int V : inputs) { 33 | START_ACC(dense); 34 | 35 | LOOP_FOR_DURATION_OR_RUNS_TRACKED (runtime, now, 1000, runs) { 36 | print_time(now, runtime, "speed mincost matching V={} ({} runs)", V, runs); 37 | 38 | vector> cost(V, vector(V)); 39 | for (int u = 0; u < V; u++) { 40 | for (int v = 0; v < V; v++) { 41 | cost[u][v] = u == v ? 0 : rand_unif(1, 50'000'000); 42 | } 43 | } 44 | 45 | ADD_TIME_BLOCK(dense) { fast_dense_hungarian(cost); } 46 | } 47 | 48 | table[{"hungarian", V}] = FORMAT_EACH(dense, runs); 49 | } 50 | 51 | print_time_table(table, "Mincost bipartite matching"); 52 | } 53 | 54 | int main() { 55 | RUN_BLOCK(stress_test_mincost_matching()); 56 | RUN_BLOCK(speed_test_mincost_matching()); 57 | return 0; 58 | } 59 | -------------------------------------------------------------------------------- /code/test/fft.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utils.hpp" 2 | #include "numeric/fft.hpp" 3 | 4 | template 5 | void stress_test_fft_multiply(int V) { 6 | LOOP_FOR_DURATION_OR_RUNS_TRACKED (20s, now, 100'000, runs) { 7 | print_time(now, 20s, "stress fft ({} runs)", runs); 8 | 9 | int A = rand_unif(0, 1000); 10 | int B = rand_unif(0, 1000); 11 | auto a = rands_unif(A, -V, V); 12 | auto b = rands_unif(B, -V, V); 13 | auto c = fft::fft_multiply(a, b); 14 | auto d = fft::naive_multiply(a, b); 15 | 16 | assert(c == d); 17 | } 18 | } 19 | 20 | template 21 | void speed_test_fft_multiply(Num V) { 22 | vector As = {6000, 15000, 30000, 50000, 100000, 200000, 300000, 500000, 800000}; 23 | vector Bs = {6000, 15000, 30000, 50000, 100000, 200000, 300000, 500000, 800000}; 24 | 25 | vector> inputs; 26 | for (int A : As) { 27 | for (int B : Bs) { 28 | inputs.push_back({A, B}); 29 | } 30 | } 31 | 32 | const auto runtime = 120'000ms / (As.size() * Bs.size()); 33 | map, string> table; 34 | 35 | for (auto [A, B] : inputs) { 36 | START_ACC(fft); 37 | 38 | LOOP_FOR_DURATION_OR_RUNS_TRACKED (runtime, now, 10000, runs) { 39 | print_time(now, runtime, "speed fft A={} B={} ({} runs)", A, B, runs); 40 | 41 | auto a = rands_unif(A, -V, V); 42 | auto b = rands_unif(B, -V, V); 43 | 44 | ADD_TIME_BLOCK(fft) { fft::fft_multiply(a, b); } 45 | } 46 | 47 | table[{A, B}] = FORMAT_EACH(fft, runs); 48 | } 49 | 50 | print_time_table(table, "FFT multiply"); 51 | } 52 | 53 | int main() { 54 | RUN_BLOCK(stress_test_fft_multiply(1000)); 55 | RUN_BLOCK(stress_test_fft_multiply(100'000)); 56 | RUN_BLOCK(speed_test_fft_multiply(100'000)); 57 | return 0; 58 | } 59 | -------------------------------------------------------------------------------- /code/test/test_utils.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "random.hpp" 4 | #include "lib/test_chrono.hpp" 5 | #include "lib/test_progress.hpp" 6 | 7 | template 8 | bool all_eq(const Container& v) { 9 | return v.empty() || equal(next(begin(v)), end(v), begin(v)); 10 | } 11 | 12 | template 13 | void print_time_table(const map, String>& times, const string& label) { 14 | printcl("===== {}\n{}=====\n", label, format_pair_map(times)); 15 | } 16 | 17 | template 18 | void print_time_table(const map, String>& times, const string& label) { 19 | printcl("===== {}\n{}=====\n", label, format_tuple_map(times, true)); 20 | } 21 | 22 | template 23 | void print_time_table(const vector>& times, const string& label) { 24 | printcl("===== {}\n{}=====\n", label, mat_to_string(times)); 25 | } 26 | 27 | #define RUN_BLOCK(test) \ 28 | do { \ 29 | printcl("{:<10} === {}\n", "RUN", #test); \ 30 | START(runner); \ 31 | test; \ 32 | TIME(runner); \ 33 | printcl("OK {:>6.2f}s === {}\n", 1e-3 * TIME_MS(runner), #test); \ 34 | } while (0) 35 | 36 | #define RUN_SHORT(test) \ 37 | do { \ 38 | START(runner); \ 39 | test; \ 40 | TIME(runner); \ 41 | printcl("OK {:>6.2f}s === {}\n", 1e-3 * TIME_MS(runner), #test); \ 42 | } while (0) 43 | -------------------------------------------------------------------------------- /code/graphs/centroid_decomposition.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "algo/y_combinator.hpp" 4 | 5 | auto build_centroid_decomposition(const vector>& tree) { 6 | int N = tree.size(); 7 | vector parent(N, -1), depth(N, -1), subsize(N); 8 | 9 | auto subsize_dfs = y_combinator([&](auto self, int u, int p) -> void { 10 | subsize[u] = 1; 11 | for (int v : tree[u]) { 12 | if (v != p) { 13 | self(v, u); 14 | subsize[u] += subsize[v]; 15 | } 16 | } 17 | }); 18 | 19 | using P = pair; 20 | 21 | auto centroid_dfs = y_combinator([&](auto self, int u, int p, int cp, int S) -> P { 22 | int processed = 0; 23 | 24 | bool changed; 25 | do { 26 | changed = false; 27 | for (int v : tree[u]) { 28 | while (v != p && depth[v] == -1 && subsize[v] > S / 2) { 29 | auto [more, root] = self(v, u, cp, S); 30 | subsize[u] -= more; 31 | S -= more, processed += more; 32 | cp = root; 33 | changed = true; 34 | } 35 | } 36 | } while (changed); 37 | 38 | // backtrack if u is not a centroid child of cp; else recurse on children 39 | if (S - subsize[u] > S / 2) { 40 | return make_pair(processed, cp); 41 | } 42 | 43 | parent[u] = cp; 44 | depth[u] = cp != -1 ? depth[cp] + 1 : 0; 45 | 46 | for (int v : tree[u]) { 47 | if (v != p && depth[v] == -1) { 48 | self(v, u, u, subsize[v]); 49 | } 50 | } 51 | 52 | return make_pair(processed + subsize[u], u); 53 | }); 54 | 55 | for (int u = 0; u < N; u++) { 56 | if (depth[u] == -1) { 57 | subsize_dfs(u, -1); 58 | centroid_dfs(u, -1, -1, subsize[u]); 59 | } 60 | } 61 | 62 | return make_pair(move(parent), move(depth)); 63 | } 64 | -------------------------------------------------------------------------------- /code/geometry/format2d.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "formatting.hpp" 4 | #include "geometry/geometry2d.hpp" 5 | #include "geometry/double2d.hpp" 6 | 7 | template 8 | auto format_obj_vertex(const Pt& pt) { 9 | return format("v {} {} 0\n", pt[0], pt[1]); 10 | } 11 | 12 | auto format_obj_face(const vector& face, int offset = 1) { 13 | string s = "f"; 14 | for (int v : face) { 15 | s += ' ' + to_string(v + offset); 16 | } 17 | return s + '\n'; 18 | } 19 | 20 | template 21 | auto format_obj_vertices(const vector& pts) { 22 | string s; 23 | for (const auto& pt : pts) { 24 | s += format_obj_vertex(pt); 25 | } 26 | return s; 27 | } 28 | 29 | auto format_obj_faces(const vector>& faces, int offset = 1) { 30 | string s; 31 | for (const auto& face : faces) { 32 | s += format_obj_face(face, offset); 33 | } 34 | return s; 35 | } 36 | 37 | auto format_obj_edges(const vector>& segments, int offset = 1) { 38 | string s; 39 | for (auto [u, v] : segments) { 40 | s += format("f {} {}\n", u + offset, v + offset); 41 | } 42 | return s; 43 | } 44 | 45 | template 46 | auto write_obj_file(const string& file, const vector& pts) { 47 | ofstream out(file); 48 | assert(out.is_open()); 49 | out << format_obj_vertices(pts); 50 | } 51 | 52 | template 53 | auto write_obj_file(const string& file, const vector& pts, 54 | const vector>& faces, int offset = 1) { 55 | ofstream out(file); 56 | assert(out.is_open()); 57 | out << format_obj_vertices(pts) << '\n' << format_obj_faces(faces, offset); 58 | } 59 | 60 | template 61 | auto write_obj_file(const string& file, const vector& pts, 62 | const vector>& segments, int offset = 1) { 63 | ofstream out(file); 64 | assert(out.is_open()); 65 | out << format_obj_vertices(pts) << '\n' << format_obj_edges(segments, offset); 66 | } 67 | -------------------------------------------------------------------------------- /code/lib/event_time_tracker.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "lib/test_progress.hpp" 4 | #include "lib/test_chrono.hpp" 5 | 6 | struct event_time_tracker { 7 | discrete_distribution eventd; 8 | chrono::steady_clock::time_point start_timepoint; 9 | vector event_time_elapsed; 10 | vector event_frequency; 11 | int latest, N; 12 | 13 | event_time_tracker(initializer_list arr) : eventd(begin(arr), end(arr)) { 14 | N = eventd.probabilities().size(); 15 | event_time_elapsed.resize(N), event_frequency.resize(N); 16 | } 17 | template 18 | explicit event_time_tracker(const Array& arr) : eventd(begin(arr), end(arr)) { 19 | N = eventd.probabilities().size(); 20 | event_time_elapsed.resize(N), event_frequency.resize(N); 21 | } 22 | 23 | void set_event(int event) { latest = event; } 24 | 25 | int next_event() { return latest = eventd(mt); } 26 | 27 | void start_event(int event) { set_event(event), start(); } 28 | 29 | void start() { start_timepoint = chrono::steady_clock::now(); } 30 | 31 | void time() { 32 | auto duration = chrono::steady_clock::now() - start_timepoint; 33 | event_frequency[latest]++; 34 | event_time_elapsed[latest] += duration; 35 | } 36 | 37 | template 38 | void pretty_log(Names namesmap) const { 39 | for (int i = 0; i < N; i++) { 40 | if (event_frequency[i] > 0) { 41 | string name = namesmap[Key(i)]; 42 | long frequency = event_frequency[i]; 43 | double total_ns = event_time_elapsed[i].count(); 44 | double total_ms = total_ns / 1e6; 45 | double each_ns = frequency ? (total_ns / frequency) : 0; 46 | double each_1000ms = each_ns / 1e3; 47 | printcl("{:15} x{:<8} {:8.2f}ms {:9.2f}ms/1000\n", // 48 | name, frequency, total_ms, each_1000ms); 49 | } 50 | } 51 | } 52 | }; 53 | -------------------------------------------------------------------------------- /code/test/fft_split.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utils.hpp" 2 | #include "numeric/fft.hpp" 3 | 4 | void stress_test_fft_split_multiply() { 5 | constexpr int N = 1000; 6 | constexpr int V = 40'000'000; 7 | static_assert(__int128_t(N) * V * V <= LONG_MAX / 4); 8 | 9 | LOOP_FOR_DURATION_OR_RUNS_TRACKED (20s, now, 100'000, runs) { 10 | print_time(now, 20s, "stress fft-split ({} runs)", runs); 11 | 12 | int A = rand_unif(1, N); 13 | int B = rand_unif(1, N); 14 | auto a = rands_unif(A, -V, V); 15 | auto b = rands_unif(B, -V, V); 16 | auto c = fft::fft_split_multiply(a, b); 17 | auto d = fft::naive_multiply(a, b); 18 | 19 | assert(c == d); 20 | } 21 | } 22 | 23 | void speed_test_fft_split_multiply() { 24 | vector As = {6000, 15000, 30000, 50000, 100000, 200000, 300000, 500000, 800000}; 25 | vector Bs = {6000, 15000, 30000, 50000, 100000, 200000, 300000, 500000, 800000}; 26 | 27 | vector> inputs; 28 | for (int A : As) { 29 | for (int B : Bs) { 30 | inputs.push_back({A, B}); 31 | } 32 | } 33 | 34 | const auto runtime = 120'000ms / (As.size() * Bs.size()); 35 | map, string> table; 36 | 37 | for (auto [A, B] : inputs) { 38 | START_ACC(fft_split); 39 | long V = sqrt(LONG_MAX / 4 / (A + B)); 40 | 41 | LOOP_FOR_DURATION_OR_RUNS_TRACKED (runtime, now, 10000, runs) { 42 | print_time(now, runtime, "speed fft-split A={} B={} ({} runs)", A, B, runs); 43 | 44 | auto a = rands_unif(A, -V, V); 45 | auto b = rands_unif(B, -V, V); 46 | 47 | ADD_TIME_BLOCK(fft_split) { fft::fft_split_multiply(a, b); } 48 | } 49 | 50 | table[{A, B}] = FORMAT_EACH(fft_split, runs); 51 | } 52 | 53 | print_time_table(table, "FFT-split multiply"); 54 | } 55 | 56 | int main() { 57 | RUN_BLOCK(stress_test_fft_split_multiply()); 58 | RUN_BLOCK(speed_test_fft_split_multiply()); 59 | return 0; 60 | } 61 | -------------------------------------------------------------------------------- /code/geometry/format3d.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "formatting.hpp" 4 | #include "geometry/geometry3d.hpp" 5 | #include "geometry/double3d.hpp" 6 | 7 | template 8 | auto format_obj_vertex(const Pt& pt) { 9 | return format("v {} {} {}\n", pt[0], pt[1], pt[2]); 10 | } 11 | 12 | auto format_obj_face(const vector& face, int offset = 1) { 13 | string s = "f"; 14 | for (int v : face) { 15 | s += ' ' + to_string(v + offset); 16 | } 17 | return s + '\n'; 18 | } 19 | 20 | template 21 | auto format_obj_vertices(const vector& pts) { 22 | string s; 23 | for (const auto& pt : pts) { 24 | s += format_obj_vertex(pt); 25 | } 26 | return s; 27 | } 28 | 29 | auto format_obj_faces(const vector>& faces, int offset = 1) { 30 | string s; 31 | for (const auto& face : faces) { 32 | s += format_obj_face(face, offset); 33 | } 34 | return s; 35 | } 36 | 37 | auto format_obj_edges(const vector>& segments, int offset = 1) { 38 | string s; 39 | for (auto [u, v] : segments) { 40 | s += format("f {} {}\n", u + offset, v + offset); 41 | } 42 | return s; 43 | } 44 | 45 | template 46 | auto write_obj_file(const string& file, const vector& pts, 47 | const vector>& faces, int offset = 1) { 48 | ofstream out(file); 49 | assert(out.is_open()); 50 | out << format_obj_vertices(pts) << '\n' << format_obj_faces(faces, offset); 51 | } 52 | 53 | template 54 | auto write_obj_file(const string& file, const vector& pts) { 55 | ofstream out(file); 56 | assert(out.is_open()); 57 | out << format_obj_vertices(pts) << '\n'; 58 | } 59 | 60 | template 61 | auto write_obj_file(const string& file, const vector& pts, 62 | const vector>& segments, int offset = 1) { 63 | ofstream out(file); 64 | assert(out.is_open()); 65 | out << format_obj_vertices(pts) << '\n' << format_obj_edges(segments, offset); 66 | } 67 | -------------------------------------------------------------------------------- /code/strings/kmp.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | using namespace std; 5 | 6 | /** 7 | * Knuth-Morris-Pratt 8 | * https://en.wikipedia.org/wiki/Knuth-Morris-Pratt_algorithm 9 | */ 10 | class KMP { 11 | vector table; 12 | string needle; 13 | 14 | public: 15 | KMP(string pattern) : needle(move(pattern)) { 16 | int P = needle.size(); 17 | table.resize(P + 1); 18 | table[0] = -1; 19 | 20 | int b = 0; 21 | for (int j = 1; j < P; ++j, ++b) { 22 | if (needle[j] == needle[b]) { 23 | table[j] = table[b]; 24 | } else { 25 | table[j] = b; 26 | do { 27 | b = table[b]; 28 | } while (b >= 0 && needle[j] != needle[b]); 29 | } 30 | } 31 | table[P] = b; 32 | } 33 | 34 | int lookup(int j) const { return table[j]; } 35 | int shift(int j) const { return j - table[j]; } 36 | const string& get_pattern() const { return needle; } 37 | }; 38 | 39 | int kmp_search(const string& text, const KMP& kmp) { 40 | const string& needle = kmp.get_pattern(); 41 | int P = needle.size(), T = text.size(); 42 | int i = 0, j = 0; 43 | 44 | while (i <= T - P) { 45 | while (j < P && text[i + j] == needle[j]) { 46 | j++; 47 | } 48 | if (j == P) { 49 | return i; 50 | } 51 | i += kmp.shift(j); 52 | j = kmp.lookup(j); 53 | } 54 | 55 | return -1; 56 | } 57 | 58 | vector kmp_search_all(const string& text, const KMP& kmp) { 59 | const string& needle = kmp.get_pattern(); 60 | int P = needle.size(), T = text.size(); 61 | int i = 0, j = 0; 62 | vector match; 63 | 64 | while (i <= T - P) { 65 | while (j < P && text[i + j] == needle[j]) { 66 | j++; 67 | } 68 | if (j == P) { 69 | match.push_back(i); 70 | } 71 | i += kmp.shift(j); 72 | j = max(0, kmp.lookup(j)); 73 | } 74 | 75 | return match; 76 | } 77 | -------------------------------------------------------------------------------- /code/test/simplex.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utils.hpp" 2 | #include "linear/simplex.hpp" 3 | 4 | void run_test(simplex& lp) { 5 | auto normal_primal = lp; 6 | auto normal_dual = lp; 7 | auto dual_primal = lp; 8 | auto dual_dual = lp; 9 | dual_primal.dualize(); 10 | dual_dual.dualize(); 11 | 12 | println("== N={} M={} phi={}", lp.N, lp.M, lp.potential()); 13 | 14 | auto ok1 = normal_primal.run_primal_dual(); 15 | println("Normal run_primal_dual()"); 16 | println(" result: {}", to_string(ok1)); 17 | println(" ans: {}", normal_primal.extract()); 18 | println(" opt: {}", normal_primal.optimum); 19 | 20 | auto ok2 = normal_dual.run_dual_primal(); 21 | println("Normal run_dual_primal()"); 22 | println(" result: {}", to_string(ok2)); 23 | println(" ans: {}", normal_dual.extract()); 24 | println(" opt: {}", normal_dual.optimum); 25 | 26 | auto ok3 = dual_primal.run_primal_dual(); 27 | println("Dual run_primal_dual()"); 28 | println(" result: {}", to_string(ok3)); 29 | println(" ans: {}", dual_primal.extract()); 30 | println(" opt: {}", dual_primal.optimum); 31 | 32 | auto ok4 = dual_dual.run_dual_primal(); 33 | println("Dual run_dual_primal()"); 34 | println(" result: {}", to_string(ok4)); 35 | println(" ans: {}", dual_dual.extract()); 36 | println(" opt: {}", dual_dual.optimum); 37 | } 38 | 39 | void unit_test_simplex() { 40 | simplex lp(3, 3); 41 | 42 | lp.A = { 43 | {3, 2, 6}, 44 | {2, -1, 7}, 45 | {4, 5, 1}, 46 | }; 47 | lp.B = {15, 4, 13}; 48 | lp.C = {2, 4, 3}; 49 | 50 | run_test(lp); // 12.416667, (0, 2.4166667, 0.9166667) 51 | lp.run_primal_dual(); 52 | 53 | lp.add_dual_variable(6, {5, 4, 3}); 54 | run_test(lp); // 6, (0, 1.5, 0) 55 | lp.run_primal_dual(); 56 | 57 | lp.add_primal_variable(7, {4, 1, 3, 1}); 58 | run_test(lp); // 26.5, (0, 0.125, 0, 4.125) 59 | lp.run_primal_dual(); 60 | } 61 | 62 | int main() { 63 | RUN_BLOCK(unit_test_simplex()); 64 | return 0; 65 | } 66 | -------------------------------------------------------------------------------- /code/numeric/complex.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | using namespace std; 5 | 6 | // A polyfill of std::complex, in case it is too slow because bananas or is not available 7 | 8 | inline namespace complex_polyfill { 9 | 10 | template 11 | struct my_complex { 12 | using self = my_complex; 13 | T x, y; 14 | my_complex(T x = T(0), T y = T(0)) : x(x), y(y) {} 15 | 16 | T& real() { return x; } 17 | T& imag() { return y; } 18 | const T& real() const { return x; } 19 | const T& imag() const { return y; } 20 | friend auto real(const self& a) { return a.x; } 21 | friend auto imag(const self& a) { return a.y; } 22 | friend auto abs(const self& a) { return sqrt(norm(a)); } 23 | friend auto arg(const self& a) { return atan2(a.y, a.x); } 24 | friend auto norm(const self& a) { return a.x * a.x + a.y * a.y; } 25 | friend auto conj(const self& a) { return self(a.x, -a.y); } 26 | friend auto inv(const self& a) { 27 | auto n = norm(a); 28 | return self(a.x / n, -a.y / n); 29 | } 30 | 31 | friend auto polar(const T& r, const T& theta = T()) { 32 | return self(r * cos(theta), r * sin(theta)); 33 | } 34 | 35 | self& operator+=(const self& b) { return *this = *this + b; } 36 | self& operator-=(const self& b) { return *this = *this - b; } 37 | self& operator*=(const self& b) { return *this = *this * b; } 38 | self& operator/=(const self& b) { return *this = *this / b; } 39 | 40 | friend self operator+(const self& a) { return a; } 41 | friend self operator-(const self& a) { return -a; } 42 | friend self operator+(const self& a, const self& b) { 43 | return self(a.x + b.x, a.y + b.y); 44 | } 45 | friend self operator-(const self& a, const self& b) { 46 | return self(a.x - b.x, a.y - b.y); 47 | } 48 | friend self operator*(const self& a, const self& b) { 49 | return self(a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x); 50 | } 51 | friend self operator/(self a, self b) { return a * inv(b); } 52 | }; 53 | 54 | } // namespace complex_polyfill 55 | -------------------------------------------------------------------------------- /code/test/binary_indexed_tree.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utils.hpp" 2 | #include "struct/binary_indexed_tree.hpp" 3 | 4 | void stress_test_bitree() { 5 | constexpr int N = 50, RUNS = 10'000'000, MAX = (1 << 25) - 1; 6 | 7 | vector arr(N); 8 | for (int i = 0; i < N; i++) { 9 | arr[i] = (i + 1) ^ 1337; 10 | } 11 | 12 | bitree> bit(N, arr, bit_xor{}, 0); 13 | 14 | for (int run = 0; run < RUNS; run++) { 15 | print_regular(run, RUNS, 10'000, "stress test bitree"); 16 | 17 | int r = rand_unif(0, N - 1); 18 | int v = rand_unif(0, MAX); 19 | bit.combine(r, v); 20 | arr[r] ^= v; 21 | 22 | r = rand_unif(1, N); 23 | int x = bit.prefix(r); 24 | int y = 0; 25 | for (int i = 0; i < r; i++) { 26 | y ^= arr[i]; 27 | } 28 | assert(x == y); 29 | } 30 | } 31 | 32 | void stress_test_bitree2d() { 33 | constexpr int N = 20, M = 20, RUNS = 10'000'000, MAX = (1 << 25) - 1; 34 | 35 | vector> arr(N, vector(M)); 36 | for (int i = 0; i < N; i++) { 37 | for (int j = 0; j < M; j++) { 38 | arr[i][j] = (i + 1) ^ (2 * j + 1); 39 | } 40 | } 41 | 42 | bitree2d> bit(N, M, arr, bit_xor{}, 0); 43 | 44 | for (int run = 0; run < RUNS; run++) { 45 | print_regular(run, RUNS, 10'000, "stress test bitree2d"); 46 | 47 | int r = rand_unif(0, N - 1); 48 | int c = rand_unif(0, M - 1); 49 | int v = rand_unif(0, MAX); 50 | bit.combine(r, c, v); 51 | arr[r][c] ^= v; 52 | 53 | r = rand_unif(1, N); 54 | c = rand_unif(1, M); 55 | int x = bit.prefix(r, c); 56 | int y = 0; 57 | for (int i = 0; i < r; i++) { 58 | for (int j = 0; j < c; j++) { 59 | y ^= arr[i][j]; 60 | } 61 | } 62 | assert(x == y); 63 | } 64 | } 65 | 66 | int main() { 67 | RUN_BLOCK(stress_test_bitree()); 68 | RUN_BLOCK(stress_test_bitree2d()); 69 | return 0; 70 | } 71 | -------------------------------------------------------------------------------- /code/geometry/algo3d.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "geometry/geometry3d.hpp" 4 | 5 | auto extract_points(const vector>& facets, const vector& pts) { 6 | int F = facets.size(); 7 | vector> ans(F); 8 | for (int f = 0; f < F; f++) { 9 | int N = facets[f].size(); 10 | ans[f].resize(N); 11 | for (int i = 0; i < N; i++) { 12 | ans[f][i] = pts[facets[f][i]]; 13 | } 14 | } 15 | return ans; 16 | } 17 | 18 | auto area3d(const vector>& facets, const vector& pts) { 19 | Pt3::L hull_area = 0; 20 | for (const auto& face : facets) { 21 | for (int i = 1, F = face.size(); i + 1 < F; i++) { 22 | const auto &a = pts[face[0]], b = pts[face[i]], c = pts[face[i + 1]]; 23 | hull_area += norm(cross(a, b, c)); 24 | } 25 | } 26 | return hull_area / 2.0; 27 | } 28 | 29 | auto volume3d(const vector>& facets, const vector& pts) { 30 | Pt3::L hull_volume = 0; 31 | for (const auto& face : facets) { 32 | for (int i = 1, F = face.size(); i + 1 < F; i++) { 33 | const auto &a = pts[face[0]], b = pts[face[i]], c = pts[face[i + 1]]; 34 | hull_volume += dot(a, cross(b, c)); 35 | } 36 | } 37 | return hull_volume / 6.0; 38 | } 39 | 40 | auto centroid3d(const vector>& facets, const vector& pts) { 41 | array ans = {}; 42 | auto sq = [](const Pt3::L& x) { return x * x; }; 43 | for (const auto& face : facets) { 44 | for (int i = 1, F = face.size(); i + 1 < F; i++) { 45 | const auto &a = pts[face[0]], b = pts[face[i]], c = pts[face[i + 1]]; 46 | auto n = cross(a, b, c); 47 | ans[0] += n.x * (sq(a.x + b.x) + sq(b.x + c.x) + sq(c.x + a.x)); 48 | ans[1] += n.y * (sq(a.y + b.y) + sq(b.y + c.y) + sq(c.y + a.y)); 49 | ans[2] += n.z * (sq(a.z + b.z) + sq(b.z + c.z) + sq(c.z + a.z)); 50 | } 51 | } 52 | auto V = volume3d(facets, pts); 53 | ans[0] /= 48 * V, ans[1] /= 48 * V, ans[2] /= 48 * V; 54 | return ans; 55 | } 56 | -------------------------------------------------------------------------------- /code/struct/min_queue.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | using namespace std; 5 | 6 | template > 7 | struct min_queue { 8 | vector fs, bs; // front stack, back stack 9 | T recent; 10 | Compare cmp; 11 | 12 | explicit min_queue(const Compare& cmp = Compare()) : cmp(cmp) {} 13 | 14 | auto pop() { 15 | T x = top(); 16 | transfer(), fs.pop_back(); 17 | return x; 18 | } 19 | void push(T x) { recent = bs.empty() ? x : min(recent, x, cmp), bs.push_back(x); } 20 | auto top() const { 21 | return bs.empty() ? fs.back() : fs.empty() ? recent : min(fs.back(), recent, cmp); 22 | } 23 | int size() const { return fs.size() + bs.size(); } 24 | bool empty() const { return fs.empty() && bs.empty(); } 25 | 26 | private: 27 | void transfer() { 28 | if (fs.empty()) { 29 | while (!bs.empty()) { 30 | T x = bs.back(); 31 | bs.pop_back(); 32 | fs.push_back(fs.empty() ? x : min(x, fs.back(), cmp)); 33 | } 34 | } 35 | } 36 | }; 37 | 38 | template 39 | struct window_queue { // Aggregates (older, newer) 40 | vector fs, bs; // front stack, back stack 41 | T recent; 42 | BinOp binop; 43 | 44 | explicit window_queue(const BinOp& binop = BinOp()) : binop(binop) {} 45 | 46 | auto pop() { 47 | T x = top(); 48 | transfer(), fs.pop_back(); 49 | return x; 50 | } 51 | void push(T x) { recent = bs.empty() ? x : binop(recent, x), bs.push_back(x); } 52 | auto top() const { 53 | return bs.empty() ? fs.back() : fs.empty() ? recent : binop(fs.back(), recent); 54 | } 55 | int size() const { return fs.size() + bs.size(); } 56 | bool empty() const { return fs.empty() && bs.empty(); } 57 | 58 | private: 59 | void transfer() { 60 | if (fs.empty()) { 61 | while (!bs.empty()) { 62 | T x = bs.back(); 63 | bs.pop_back(); 64 | fs.push_back(fs.empty() ? x : binop(x, fs.back())); 65 | } 66 | } 67 | } 68 | }; 69 | -------------------------------------------------------------------------------- /code/graphs/isomorphism.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "hash.hpp" 4 | #include "linear/matrix.hpp" 5 | 6 | using edges_t = vector>; 7 | 8 | // O(V³ log V) topology, has false positives 9 | struct graph_topology { 10 | using num = modnum<999999893>; 11 | static inline unordered_map, int, Hasher> node_cache, graph_cache; 12 | using Edges = vector>; 13 | using Graph = vector>; 14 | 15 | static auto make_graph(int V, const Edges& edges) { 16 | Graph graph(V); 17 | for (auto [u, v] : edges) { 18 | graph[u].push_back(v); 19 | graph[v].push_back(u); 20 | } 21 | return graph; 22 | } 23 | 24 | static auto get_node(const vector& node) { 25 | if (auto pos = node_cache.find(node); pos != end(node_cache)) { 26 | return pos->second; 27 | } else { 28 | int S = node_cache.size(); 29 | return node_cache[node] = S; 30 | } 31 | } 32 | 33 | static auto get_graph(const vector& graph) { 34 | if (auto pos = graph_cache.find(graph); pos != end(graph_cache)) { 35 | return pos->second; 36 | } else { 37 | int S = graph_cache.size(); 38 | return graph_cache[graph] = S; 39 | } 40 | } 41 | 42 | static auto undirected_vertex_hashes(int V, const Edges& edges) { 43 | mat A({V, V}); 44 | for (auto [u, v] : edges) { 45 | A[u][v] += 1, A[v][u] += 1; 46 | } 47 | A = A ^ V; 48 | vector ids(V); 49 | for (int u = 0; u < V; u++) { 50 | vector vs; 51 | for (int i = 0; i < V; i++) { 52 | vs.push_back(int(A[u][i])); 53 | } 54 | swap(vs[0], vs[u]); 55 | sort(begin(vs) + 1, end(vs)); 56 | ids[u] = get_node(vs); 57 | } 58 | return ids; 59 | } 60 | 61 | static auto undirected_graph_hash(int V, const Edges& edges) { 62 | auto ids = undirected_vertex_hashes(V, edges); 63 | sort(begin(ids), end(ids)); 64 | return get_graph(ids); 65 | } 66 | }; 67 | -------------------------------------------------------------------------------- /code/graphs/min_spanning_forest.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "struct/disjoint_set.hpp" 4 | 5 | using edges_t = vector>; 6 | 7 | long min_spanning_forest_kruskal(int V, const edges_t& g, const vector& weight) { 8 | int E = g.size(); 9 | disjoint_set set(V); 10 | vector edges(E); 11 | iota(begin(edges), end(edges), 0); 12 | sort(begin(edges), end(edges), 13 | [&weight](int e1, int e2) { return weight[e1] < weight[e2]; }); 14 | 15 | long msf = 0; 16 | for (int e : edges) { 17 | auto [u, v] = g[e]; 18 | if (set.find(u) != set.find(v)) { 19 | msf += weight[e]; 20 | set.join(u, v); 21 | } 22 | } 23 | return msf; 24 | } 25 | 26 | long min_spanning_forest_prim(int V, const edges_t& g, const vector& weight) { 27 | vector>> adj(V); 28 | for (int e = 0, E = g.size(); e < E; e++) { 29 | auto [u, v] = g[e]; 30 | adj[u].push_back({v, weight[e]}); 31 | adj[v].push_back({u, weight[e]}); 32 | } 33 | 34 | vector vis(V, false); 35 | long msf = 0; 36 | for (int n = 0; n < V; n++) { 37 | if (vis[n]) 38 | continue; 39 | 40 | priority_queue> Q; 41 | Q.push({0, n}); 42 | 43 | while (!Q.empty()) { 44 | auto [neg_weight, u] = Q.top(); 45 | Q.pop(); 46 | if (vis[u]) 47 | continue; 48 | 49 | vis[u] = true; 50 | msf += -neg_weight; 51 | for (auto [v, w] : adj[u]) 52 | if (!vis[v]) 53 | Q.push({-w, v}); 54 | } 55 | } 56 | return msf; 57 | } 58 | 59 | long min_spanning_forest_dense(int V, const vector>& weight) { 60 | static constexpr long inf = LONG_MAX / 2; 61 | vector pi = weight[0]; 62 | pi[0] = inf; 63 | long msf = 0; 64 | for (int n = 1; n < V; n++) { 65 | int u = min_element(begin(pi), end(pi)) - begin(pi); 66 | msf += pi[u], pi[u] = inf; 67 | for (int v = 0; v < V; v++) 68 | if (pi[v] != inf) 69 | pi[v] = min(pi[v], weight[u][v]); 70 | } 71 | return msf; 72 | } 73 | -------------------------------------------------------------------------------- /code/algo/virtual_tree.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "struct/disjoint_set.hpp" 4 | #include "algo/y_combinator.hpp" 5 | 6 | // Generic edge-decomposed binary tree. 7 | // Edge e is assigned vertex N+e, and the tree has 2N-1 vertices 8 | auto build_edge_virtual_tree(int N, const vector>& tree, 9 | vector order) { 10 | disjoint_set dsu(N); 11 | reverse(begin(order), end(order)); 12 | 13 | vector top(N); 14 | iota(begin(top), end(top), 0); 15 | 16 | int V = 2 * N - 1; 17 | vector> kids(V, {-1, -1}); 18 | vector parent(V, -1); 19 | 20 | for (int e : order) { 21 | int u = tree[e][0], v = tree[e][1]; 22 | u = dsu.find(u), v = dsu.find(v); 23 | dsu.join(u, v); 24 | int a = top[u], b = top[v]; 25 | kids[N + e] = {a, b}; 26 | top[u] = top[v] = parent[a] = parent[b] = N + e; 27 | } 28 | 29 | int root = N + order.back(); 30 | return make_tuple(root, move(parent), move(kids)); 31 | } 32 | 33 | // min edge-decomposed binary tree (i.e. root of tree is minimum weight edge) 34 | template > 35 | auto build_min_edge_virtual_tree(int N, const vector>& edges, 36 | const vector& weight, 37 | const Compare& cmp = Compare()) { 38 | int E = N - 1; 39 | assert(E == int(edges.size()) && E == int(weight.size())); 40 | 41 | vector order(E); 42 | iota(begin(order), end(order), 0); 43 | sort(begin(order), end(order), 44 | [&](int a, int b) { return cmp(weight[a], weight[b]); }); 45 | 46 | auto [root, parent, kids] = build_edge_virtual_tree(N, edges, order); 47 | 48 | int V = 2 * N - 1; 49 | vector intime(V), pretime(V); 50 | int intimer = 0, pretimer = 0; 51 | 52 | y_combinator([&, &kids = kids](auto self, int i) -> void { 53 | if (i != -1) { 54 | pretime[pretimer++] = i; 55 | self(kids[i][0]); 56 | intime[intimer++] = i; 57 | self(kids[i][1]); 58 | } 59 | })(root); 60 | 61 | return make_tuple(root, move(parent), move(kids), move(intime), move(pretime)); 62 | } 63 | -------------------------------------------------------------------------------- /code/test/combinatorics.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utils.hpp" 2 | #include "algo/y_combinator.hpp" 3 | #include "numeric/combinatorics.hpp" 4 | #include "numeric/modnum.hpp" 5 | 6 | using num = modnum<998244353>; 7 | using B = Binomial; 8 | using P = Partition; 9 | using D = Permutations; 10 | 11 | auto naive_layouts(int n, int k, int a, int b) { 12 | b = min(b, n); 13 | return y_combinator([&](auto self, int i, int r) -> num { 14 | if (i == k) { 15 | return r == 0; 16 | } 17 | int need = (k - i - 1) * a; 18 | num ans = 0; 19 | for (int x = a; x <= b && r - x >= need; x++) { 20 | ans += self(i + 1, r - x); 21 | } 22 | return ans; 23 | })(0, n); 24 | } 25 | 26 | void stress_test_layouts() { 27 | for (int n = 0; n <= 11; n++) { 28 | for (int k = 0; k <= n + 1; k++) { 29 | for (int a = 0; a <= n + 1; a++) { 30 | assert(B::layout(n, k, a) == naive_layouts(n, k, a, n)); 31 | for (int b = a; b <= n + 1; b++) { 32 | assert(B::bounded_layout(n, k, a, b) == naive_layouts(n, k, a, b)); 33 | } 34 | } 35 | } 36 | } 37 | } 38 | 39 | void unit_test_combinatorics() { 40 | B::ensure_factorial(16); 41 | D::ensure_derangements(16); 42 | 43 | println("factorial: {}", B::fact); 44 | println("invfactorial: {}", B::ifact); 45 | println("derangements: {}", D::deran); 46 | 47 | P::ensure_partition(32); 48 | P::ensure_partition_distinct(32); 49 | P::ensure_partition_odd_distinct(32); 50 | D::ensure_bell(16); 51 | D::ensure_stir1st(16); 52 | D::ensure_stir2nd(16); 53 | 54 | println("partitions: {}", P::partp); 55 | println("partitions distinct: {}", P::partq); 56 | println("partitions odd dist: {}", P::partd); 57 | println("bell: {}", D::belln); 58 | for (int n = 0; n <= 16; n++) { 59 | println("stir1st[{:2}]: {}", n, D::stir1st[n]); 60 | } 61 | for (int n = 0; n <= 16; n++) { 62 | println("stir2nd[{:2}]: {}", n, D::stir2nd[n]); 63 | } 64 | } 65 | 66 | int main() { 67 | RUN_BLOCK(stress_test_layouts()); 68 | RUN_BLOCK(unit_test_combinatorics()); 69 | return 0; 70 | } 71 | -------------------------------------------------------------------------------- /code/flow/edmonds_karp.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | using namespace std; 5 | 6 | // Edmonds-Karp augmenting paths. O(VE²) easy to modify I guess 7 | template 8 | struct edmonds_karp { 9 | struct Edge { 10 | int node[2]; 11 | Flow cap, flow = 0; 12 | }; 13 | int V, E = 0; 14 | vector> res; 15 | vector edge; 16 | 17 | explicit edmonds_karp(int V) : V(V), res(V) {} 18 | 19 | int add(int u, int v, Flow capacity) { 20 | assert(0 <= u && u < V && 0 <= v && v < V && capacity >= 0); 21 | res[u].push_back(E++), edge.push_back({{u, v}, capacity, 0}); 22 | res[v].push_back(E++), edge.push_back({{v, u}, 0, 0}); 23 | return (E - 2) >> 1; 24 | } 25 | 26 | vector pred, Q; 27 | static constexpr Flow flow_inf = numeric_limits::max(); 28 | 29 | bool bfs(int s, int t) { 30 | pred.assign(V, -1); 31 | Q[0] = s, pred[s] = E; 32 | for (int i = 0, S = 1; i < S && pred[t] == -1; i++) { 33 | int u = Q[i]; 34 | for (auto e : res[u]) { 35 | int v = edge[e].node[1]; 36 | if (pred[v] == -1 && v != s && edge[e].flow < edge[e].cap) { 37 | pred[v] = e; 38 | Q[S++] = v; 39 | } 40 | } 41 | } 42 | return pred[t] != -1; 43 | } 44 | 45 | Flow augment(int t) { 46 | Flow aug_flow = flow_inf; 47 | for (int e = pred[t]; e != E; e = pred[edge[e].node[0]]) { 48 | aug_flow = min(aug_flow, edge[e].cap - edge[e].flow); 49 | } 50 | for (int e = pred[t]; e != E; e = pred[edge[e].node[0]]) { 51 | edge[e].flow += aug_flow; 52 | edge[e ^ 1].flow -= aug_flow; 53 | } 54 | return aug_flow; 55 | } 56 | 57 | FlowSum maxflow(int s, int t) { 58 | Q.resize(V); 59 | FlowSum max_flow = 0; 60 | while (bfs(s, t)) { 61 | max_flow += augment(t); 62 | } 63 | return max_flow; 64 | } 65 | 66 | Flow get_flow(int e) const { return edge[2 * e].flow; } 67 | bool left_of_mincut(int u) const { return pred[u] >= 0; } 68 | }; 69 | -------------------------------------------------------------------------------- /code/linear/characteristic_polynomial.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "linear/matrix.hpp" 4 | #include "numeric/polynomial.hpp" 5 | 6 | /** 7 | * Source: https://github.com/Aeren1564/Algorithms/.../characteristic_polynomial 8 | */ 9 | template 10 | vector characteristic_polynomial(const mat& a, bool flip = false) { 11 | using namespace polymath; 12 | assert(a.n == a.m); 13 | int n = a.n; 14 | for (int j = 0; j < n; j++) { 15 | int pivot = -1; 16 | for (int i = j + 1; i < n; ++i) { 17 | if (a[i][j]) { 18 | pivot = i; 19 | break; 20 | } 21 | } 22 | if (pivot == -1) 23 | continue; 24 | for (int k = 0; pivot != j + 1 && k < n; ++k) { 25 | swap(a[j + 1][k], a[pivot][k]); 26 | } 27 | for (int k = 0; pivot != j + 1 && k < n; ++k) { 28 | swap(a[k][j + 1], a[k][pivot]); 29 | } 30 | for (int i = j + 2; i < n; ++i) { 31 | if (a[i][j]) { 32 | T t = a[i][j] / a[j + 1][j]; 33 | for (int k = 0; k < n; ++k) 34 | a[i][k] -= a[j + 1][k] * t; 35 | for (int k = 0; k < n; ++k) 36 | a[k][j + 1] += a[k][i] * t; 37 | } 38 | } 39 | } 40 | vector X, Y; 41 | for (int x = 0; x <= n; x++) { 42 | mat b(n, n); 43 | for (auto i = 0; i < n; ++i) 44 | for (auto j = 0; j < n; ++j) 45 | b[i][j] = i == j ? x - a[i][j] : -a[i][j]; 46 | 47 | T sign = 1; 48 | for (auto j = 0; j < n - 1; ++j) { 49 | if (b[j + 1][j]) { 50 | for (auto k = 0; k < n; ++k) 51 | swap(b[j][k], b[j + 1][k]); 52 | sign = -sign; 53 | } 54 | if (b[j][j] && b[j + 1][j]) { 55 | T t = b[j + 1][j] / b[j][j]; 56 | for (auto jj = j; jj < n; ++jj) 57 | b[j + 1][jj] -= b[j][jj] * t; 58 | } 59 | } 60 | T y = sign; 61 | for (auto i = 0; i < n; ++i) 62 | y *= b[i][i]; 63 | X.push_back(x), Y.push_back(y); 64 | } 65 | 66 | return polymath::interpolate(X, Y) * T(flip ? -1 : 1); 67 | } 68 | -------------------------------------------------------------------------------- /code/test/mos.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utils.hpp" 2 | #include "algo/mos.hpp" 3 | #include "random.hpp" 4 | 5 | void stress_test_mos() { 6 | vector> NQs = { 7 | {100, 100}, // 8 | {100, 500}, // 9 | {1000, 1000}, // 10 | {10000, 10000}, // 11 | {100000, 1000}, // 12 | {100000, 30000}, // 13 | {100000, 100000}, // 14 | {100000, 300000}, // 15 | }; 16 | 17 | auto compute = [](const auto& queries, const auto& order) { 18 | int Q = queries.size(); 19 | long init = queries[order[0]][1] - queries[order[0]][0]; 20 | long shifts = init; 21 | long jumps = init; 22 | long transfers = init; 23 | for (int i = 1; i < Q; i++) { 24 | auto [L, R] = queries[order[i - 1]]; 25 | auto [l, r] = queries[order[i]]; 26 | shifts += abs(L - l) + abs(R - r); 27 | jumps += min(r - l, abs(L - l) + abs(R - r)); 28 | transfers += min(abs(L - l) + abs(R - r), abs(R - L) + abs(r - l)); 29 | } 30 | return make_tuple(shifts, jumps, transfers); 31 | }; 32 | 33 | map>, stringable> table; 34 | 35 | for (auto [N, Q] : NQs) { 36 | vector> queries(Q); 37 | for (auto& [l, r] : queries) { 38 | auto [u, v] = diff_unif(0, N - 1); 39 | l = u, r = v; 40 | } 41 | 42 | auto left_block_order = mosort_left_block(N, queries); 43 | auto hilbert_order = mosort_hilbert_curve(N, queries); 44 | 45 | auto [lb_shifts, lb_jumps, lb_transfers] = compute(queries, left_block_order); 46 | auto [hi_shifts, hi_jumps, hi_transfers] = compute(queries, hilbert_order); 47 | 48 | table[{"block shift", {N, Q}}] = lb_shifts; 49 | table[{"block shift/jump", {N, Q}}] = lb_jumps; 50 | table[{"block shift/transfer", {N, Q}}] = lb_transfers; 51 | table[{"hilbert shift", {N, Q}}] = hi_shifts; 52 | table[{"hilbert shift/jump", {N, Q}}] = hi_jumps; 53 | table[{"hilbert shift/transfer", {N, Q}}] = hi_transfers; 54 | } 55 | 56 | print_time_table(table, "Mo's updates"); 57 | } 58 | 59 | int main() { 60 | RUN_SHORT(stress_test_mos()); 61 | return 0; 62 | } 63 | -------------------------------------------------------------------------------- /code/test/line_tree.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utils.hpp" 2 | #include "algo/line_tree.hpp" 3 | #include "algo/virtual_tree.hpp" 4 | #include "algo/y_combinator.hpp" 5 | #include "struct/shallow_forest.hpp" 6 | #include "struct/lca.hpp" 7 | #include "struct/sparse_table.hpp" 8 | #include "lib/graph_generator.hpp" 9 | 10 | auto stress_test_line_tree() { 11 | LOOP_FOR_DURATION_OR_RUNS_TRACKED (3s, now, 10000, runs) { 12 | print_time(now, 3s, "stress line tree ({} runs)", runs); 13 | 14 | int N = rand_unif(2, 1000); 15 | auto g = random_tree(N); 16 | auto weight = int_sample(N - 1, 1, 1'000'000); 17 | auto tree = make_adjacency_lists_undirected(N, g); 18 | 19 | vector pointval(N, INT_MAX); 20 | vector>> val_tree(N); 21 | for (int e = 0; e < N - 1; e++) { 22 | auto [u, v] = g[e]; 23 | val_tree[u].push_back({v, weight[e]}); 24 | val_tree[v].push_back({u, weight[e]}); 25 | } 26 | 27 | auto minop = [](int a, int b) { return min(a, b); }; 28 | shallow_edge_sparse_table shallow(tree, pointval, val_tree, minop); 29 | 30 | auto [index, edge, value] = build_min_edge_line(N, g, weight); 31 | min_rmq rmq1(value); 32 | 33 | auto [root, parent, kids, intime, _] = build_min_edge_virtual_tree(N, g, weight); 34 | vector> virtual_tree(2 * N - 1); 35 | for (int e = 0; e < N - 1; e++) { 36 | int y = N + e, u = kids[y][0], v = kids[y][1]; 37 | virtual_tree[y].push_back(u), virtual_tree[u].push_back(y); 38 | virtual_tree[y].push_back(v), virtual_tree[v].push_back(y); 39 | } 40 | lca_incremental lca(virtual_tree, root); 41 | 42 | for (int u = 0; u < N; u++) { 43 | for (int v = 0; v < N; v++) { 44 | if (u != v) { 45 | int a = min(index[u], index[v]); 46 | int b = max(index[u], index[v]); 47 | assert(rmq1.query(a, b) == shallow.query(u, v)); 48 | assert(weight[lca.lca(u, v) - N] == shallow.query(u, v)); 49 | } 50 | } 51 | } 52 | } 53 | } 54 | 55 | int main() { 56 | RUN_BLOCK(stress_test_line_tree()); 57 | return 0; 58 | } 59 | -------------------------------------------------------------------------------- /code/algo/line_tree.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "struct/disjoint_set.hpp" 4 | 5 | // Generic edge line tree, returns {u, v, edge index} 6 | auto build_edge_line_tree(int N, const vector>& tree, vector order) { 7 | disjoint_set dsu(N); 8 | reverse(begin(order), end(order)); 9 | 10 | vector left(N), right(N); 11 | iota(begin(left), end(left), 0); 12 | iota(begin(right), end(right), 0); 13 | 14 | vector> line; 15 | 16 | for (int e : order) { 17 | int u = tree[e][0], v = tree[e][1]; 18 | u = dsu.find(u), v = dsu.find(v); 19 | dsu.join(u, v); 20 | int y = dsu.find(u); 21 | line.push_back({right[u], left[v], e}); 22 | left[y] = left[u], right[y] = right[v]; 23 | } 24 | 25 | return line; 26 | } 27 | 28 | // min edge line array for less (i.e. array retains the minimum weight edge on path) 29 | template > 30 | auto build_min_edge_line(int N, const vector>& edges, 31 | const vector& weight, const Compare& cmp = Compare()) { 32 | int E = N - 1; 33 | assert(E == int(edges.size()) && E == int(weight.size())); 34 | 35 | vector order(E); 36 | iota(begin(order), end(order), 0); 37 | sort(begin(order), end(order), 38 | [&](int a, int b) { return cmp(weight[a], weight[b]); }); 39 | 40 | auto line = build_edge_line_tree(N, edges, order); 41 | 42 | vector> adj(N, {-1, -1}); 43 | for (int i = 0; i < E; i++) { 44 | auto [u, v, e] = line[i]; 45 | adj[u][adj[u][0] != -1] = adj[v][adj[v][0] != -1] = i; 46 | } 47 | 48 | vector index(N), edge(E); 49 | vector value(E); 50 | 51 | for (int s = 0; s < N; s++) { 52 | if (adj[s][1] == -1) { 53 | int u = s, j = adj[s][0]; 54 | for (int i = 0; i < E; i++) { 55 | index[u] = i, edge[i] = line[j][2]; 56 | value[i] = weight[edge[i]]; 57 | u = line[j][u == line[j][0]]; 58 | j = adj[u][j == adj[u][0]]; 59 | } 60 | assert(j == -1); 61 | index[u] = N - 1; 62 | break; 63 | } 64 | } 65 | 66 | return make_tuple(move(index), move(edge), move(value)); 67 | } 68 | -------------------------------------------------------------------------------- /code/struct/dynamic_cht.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | using namespace std; 5 | 6 | /** 7 | * Insert-only 2d line container supporting maximum queries at given x (dynamic CHT) 8 | * Source: https://github.com/kth-competitive-programming/kactl 9 | */ 10 | template 11 | struct cht_line { 12 | T m, b; 13 | mutable T end; // largest x for which this line is in the hull 14 | bool operator<(const cht_line& other) const { return m < other.m; } 15 | bool operator<(T x) const { return end < x; } 16 | }; 17 | 18 | template 19 | struct dynamic_cht : multiset, less<>> { 20 | using Line = cht_line; 21 | using base_t = multiset, less<>>; 22 | static inline const T pinf = numeric_limits::max(); 23 | static inline const T ninf = numeric_limits::lowest(); 24 | static inline const T eps = 20 * numeric_limits::epsilon(); 25 | static inline T zerodiv(T a, T b) { 26 | if constexpr (is_integral::value) { 27 | return (a < 0) != (b < 0) ? a / b - !!(a % b) : a / b; 28 | } else { 29 | return a / b; 30 | } 31 | } 32 | bool intersect(typename base_t::iterator x, typename base_t::iterator y) { 33 | if (y == base_t::end()) { 34 | x->end = pinf; 35 | return false; 36 | } 37 | if (abs(x->m - y->m) <= eps) { 38 | x->end = x->b > y->b ? pinf : ninf; 39 | } else { 40 | x->end = zerodiv(y->b - x->b, x->m - y->m); 41 | } 42 | return x->end >= y->end; 43 | } 44 | void add(T m, T b) { 45 | auto z = base_t::insert(cht_line{m, b, 0}); 46 | auto y = z++; 47 | auto x = y; 48 | while (intersect(y, z)) { 49 | z = base_t::erase(z); 50 | } 51 | if (x != base_t::begin() && intersect(--x, y)) { 52 | intersect(x, y = base_t::erase(y)); 53 | } 54 | while ((y = x) != base_t::begin() && (--x)->end >= y->end) { 55 | intersect(x, base_t::erase(y)); 56 | } 57 | } 58 | cht_line argmax(T x) const { 59 | assert(!base_t::empty()); 60 | return *base_t::lower_bound(x); 61 | } 62 | T max(T x) const { 63 | auto line = argmax(x); 64 | return line.m * x + line.b; 65 | } 66 | }; 67 | -------------------------------------------------------------------------------- /code/test/heavy_light_decomposition.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utils.hpp" 2 | #include "lib/graph_formats.hpp" 3 | #include "lib/graph_generator.hpp" 4 | #include "graphs/heavy_light_decomposition.hpp" 5 | #include "struct/hld_forest.hpp" 6 | 7 | void unit_test_heavy_light_decomposition() { 8 | int V = 38; 9 | string sedges = "0,1 0,2 0,3 0,4 1,5 1,6 2,7 2,8 3,9 3,10 6,11 6,36 6,37 7,28 7,29 " 10 | "8,12 8,32 8,33 9,14 9,15 9,20 10,13 10,16 10,17 11,34 11,35 12,30 " 11 | "12,31 13,18 13,19 14,24 14,25 14,26 14,27 15,21 15,23 21,22"; 12 | 13 | auto g = scan_edges(sedges); 14 | random_flip_graph_inplace(g); 15 | assert(int(g.size()) == V - 1); 16 | 17 | auto tree = make_adjacency_lists_undirected(V, g); 18 | auto [parent, depth, head, tin, tout] = build_heavy_light_decomposition(tree, 0); 19 | for (int u = 0; u < V; u++) { 20 | cout << (u ? " " : " ") << setw(2) << u << " \n"[u + 1 == V]; 21 | } 22 | for (int u = 0; u < V; u++) { 23 | cout << (u ? " " : "parent ") << setw(2) << parent[u] << " \n"[u + 1 == V]; 24 | } 25 | for (int u = 0; u < V; u++) { 26 | cout << (u ? " " : " depth ") << setw(2) << depth[u] << " \n"[u + 1 == V]; 27 | } 28 | for (int u = 0; u < V; u++) { 29 | cout << (u ? " " : " head ") << setw(2) << head[u] << " \n"[u + 1 == V]; 30 | } 31 | for (int u = 0; u < V; u++) { 32 | cout << (u ? " " : " tin ") << setw(2) << tin[u] << " \n"[u + 1 == V]; 33 | } 34 | for (int u = 0; u < V; u++) { 35 | cout << (u ? " " : " tout ") << setw(2) << tout[u] << " \n"[u + 1 == V]; 36 | } 37 | } 38 | 39 | void unit_test_compress_tree() { 40 | int N = 21, root = 1; 41 | string edges = "1,2 2,11 11,13 11,14 11,15 2,16 1,5 1,3 3,17 5,4 5,8 5,7 7,19 8,9 " 42 | "9,10 9,12 10,18 10,20 9,0"; 43 | auto graph = scan_edges(edges); 44 | auto tree = make_adjacency_lists_undirected(N, graph); 45 | 46 | hld_forest hld(tree, root); 47 | 48 | auto print = [&](const vector& nodes) { 49 | println("compress({}): {}", nodes, hld.compress_tree(nodes)); 50 | }; 51 | print({2, 4, 17}); 52 | print({10, 19, 13}); 53 | } 54 | 55 | int main() { 56 | RUN_BLOCK(unit_test_heavy_light_decomposition()); 57 | RUN_BLOCK(unit_test_compress_tree()); 58 | return 0; 59 | } 60 | -------------------------------------------------------------------------------- /common/blog-graphs.txt: -------------------------------------------------------------------------------- 1 | digraph G { 2 | K=1.15; 3 | 1 [color="blue"]; 4 | 2 [color="blue"]; 5 | 3 [color="red"]; 6 | 4 [color="red"]; 7 | 5 [color="black"]; 8 | 6 [color="black"]; 9 | 7 [color="black"]; 10 | 8 [color="blue"]; 11 | 9 [color="black"]; 12 | 10 [color="red"]; 13 | 11 [color="black"]; 14 | 12 [color="blue"]; 15 | 13 [color="black"]; 16 | 14 [color="red"]; 17 | 1 -> 2; 18 | 2 -> 3; 19 | 3 -> 4; 20 | 5 -> 4; 21 | 6 -> 5; 22 | 7 -> 6; 23 | 1 -> 7; 24 | 7 -> 8 [penwidth=1.5,color="grey"]; 25 | 8 -> 6; 26 | 8 -> 9; 27 | 9 -> 10; 28 | 11 -> 10; 29 | 12 -> 11 [penwidth=1.5,color="grey"]; 30 | 5 -> 12; 31 | 12 -> 13; 32 | 13 -> 14; 33 | 4 -> 14 [penwidth=1.5,color="grey"]; 34 | 14 -> 3 [penwidth=1.5,color="grey"]; 35 | } 36 | 37 | digraph G { 38 | 0 [pos="0,5!"]; 39 | 1 [pos="1,4!"]; 40 | 2 [pos="0,3!"]; 41 | 3 [pos="-1,2!"]; 42 | 4 [pos="1,2!"]; 43 | 5 [pos="0,1!"]; 44 | 6 [label="6 u_in",pos="1,0!"]; 45 | 7 [pos="2,3!"]; 46 | 8 [pos="3,2!"]; 47 | 9 [pos="2,2!"]; 48 | 10 [label="10 v_in",pos="2,1!"]; 49 | 11 [pos="-1,4!"]; 50 | 51 | 0 -> 1; 52 | 11 -> 0; 53 | 1 -> 2 [color="red"]; 54 | 3 -> 2; 55 | 4 -> 2 [color="orange"]; 56 | 4 -> 5 [color="orange"]; 57 | 5 -> 6 [color="orange"]; 58 | 6 -> 10 [color="blue"]; 59 | 10 -> 8 [color="orange"]; 60 | 7 -> 8 [color="red"]; 61 | 7 -> 9; 62 | 1 -> 7 [color="orange"]; 63 | } 64 | 65 | digraph G { 66 | 0 [pos="0,0!",color="blue"]; 67 | 1 [pos="2,1!"]; 68 | 2 [pos="2,-1!"]; 69 | 3 [pos="4,1!",color="red"]; 70 | 4 [pos="4,-1!",color="red"]; 71 | 0 -> 1; 72 | 0 -> 2; 73 | 1 -> 2; 74 | 1 -> 3; 75 | 1 -> 4; 76 | 2 -> 3; 77 | 2 -> 4; 78 | 3 -> 4; 79 | 4 -> 2; 80 | } 81 | 82 | digraph G { 83 | s [pos="-2,0!"]; 84 | 0 [pos="0,0!"]; 85 | 1 [pos="2,1!"]; 86 | 2 [pos="2,-1!"]; 87 | 3 [pos="4,1!"]; 88 | 4 [pos="4,-1!"]; 89 | t [pos="6,0!"]; 90 | s -> 0; 91 | 0 -> 1; 92 | 0 -> 2; 93 | 1 -> 2; 94 | 1 -> 3; 95 | 1 -> 4; 96 | 2 -> 3; 97 | 2 -> 4; 98 | 3 -> 4; 99 | 4 -> 2; 100 | 3 -> t; 101 | 4 -> t; 102 | } 103 | -------------------------------------------------------------------------------- /code/test/datasets/exact_tsp.txt: -------------------------------------------------------------------------------- 1 | 4 21 2 | 4 2 1 3 3 | 0 2 9 10 4 | 1 0 6 4 5 | 15 7 0 8 6 | 6 3 12 0 7 | 8 | 9 | 4 80 10 | 4 3 1 2 11 | 0 10 15 20 12 | 10 0 35 25 13 | 15 35 0 30 14 | 20 25 30 0 15 | 16 | 17 | 5 19 18 | 1 3 2 5 4 19 | 0 3 4 2 7 20 | 3 0 4 6 3 21 | 4 4 0 5 8 22 | 2 6 5 0 6 23 | 7 3 8 6 0 24 | 25 | 26 | 15 291 27 | 1 13 2 15 9 5 7 3 12 14 10 8 6 4 11 28 | 0 29 82 46 68 52 72 42 51 55 29 74 23 72 46 29 | 29 0 55 46 42 43 43 23 23 31 41 51 11 52 21 30 | 82 55 0 68 46 55 23 43 41 29 79 21 64 31 51 31 | 46 46 68 0 82 15 72 31 62 42 21 51 51 43 64 32 | 68 42 46 82 0 74 23 52 21 46 82 58 46 65 23 33 | 52 43 55 15 74 0 61 23 55 31 33 37 51 29 59 34 | 72 43 23 72 23 61 0 42 23 31 77 37 51 46 33 35 | 42 23 43 31 52 23 42 0 33 15 37 33 33 31 37 36 | 51 23 41 62 21 55 23 33 0 29 62 46 29 51 11 37 | 55 31 29 42 46 31 31 15 29 0 51 21 41 23 37 38 | 29 41 79 21 82 33 77 37 62 51 0 65 42 59 61 39 | 74 51 21 51 58 37 37 33 46 21 65 0 61 11 55 40 | 23 11 64 51 46 51 51 33 29 41 42 61 0 62 23 41 | 72 52 31 43 65 29 46 31 51 23 59 11 62 0 59 42 | 46 21 51 64 23 59 33 37 11 37 61 55 23 59 0 43 | 44 | 45 | 17 2085 46 | 1 4 13 7 8 6 17 14 15 3 11 10 2 5 9 12 16 47 | 0 633 257 91 412 150 80 134 259 505 353 324 70 211 268 246 121 48 | 633 0 390 661 227 488 572 530 555 289 282 638 567 466 420 745 518 49 | 257 390 0 228 169 112 196 154 372 262 110 437 191 74 53 472 142 50 | 91 661 228 0 383 120 77 105 175 476 324 240 27 182 239 237 84 51 | 412 227 169 383 0 267 351 309 338 196 61 421 346 243 199 528 297 52 | 150 488 112 120 267 0 63 34 264 360 208 329 83 105 123 364 35 53 | 80 572 196 77 351 63 0 29 232 444 292 297 47 150 207 332 29 54 | 134 530 154 105 309 34 29 0 249 402 250 314 68 108 165 349 36 55 | 259 555 372 175 338 264 232 249 0 495 352 95 189 326 383 202 236 56 | 505 289 262 476 196 360 444 402 495 0 154 578 439 336 240 685 390 57 | 353 282 110 324 61 208 292 250 352 154 0 435 287 184 140 542 238 58 | 324 638 437 240 421 329 297 314 95 578 435 0 254 391 448 157 301 59 | 70 567 191 27 346 83 47 68 189 439 287 254 0 145 202 289 55 60 | 211 466 74 182 243 105 150 108 326 336 184 391 145 0 57 426 96 61 | 268 420 53 239 199 123 207 165 383 240 140 448 202 57 0 483 153 62 | 246 745 472 237 528 364 332 349 202 685 542 157 289 426 483 0 336 63 | 121 518 142 84 297 35 29 36 236 390 238 301 55 96 153 336 0 64 | -------------------------------------------------------------------------------- /code/test/mincost_flow.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utils.hpp" 2 | #include "flow/mincost_flow.hpp" 3 | #include "lib/graph_generator.hpp" 4 | 5 | void speed_test_mincost_flow() { 6 | vector Vs = {2000, 5000, 10000, 25000}; 7 | vector pVs = {2.0, 5.0, 12.0, 20.0}; 8 | vector as = {-.35, -.15, -.05, -.01, -.001, 0, +.001, +.01, +.05, +.15, +.35}; 9 | 10 | vector> inputs; 11 | for (int V : Vs) { 12 | for (double pV : pVs) { 13 | for (double a : as) { 14 | double p = pV / V, E = V * pV; 15 | if (p <= 1.0 && E <= 100'000) { 16 | inputs.push_back({V, pV, a}); 17 | } 18 | } 19 | } 20 | } 21 | 22 | const auto runtime = 180'000ms / inputs.size(); 23 | map, int, stringable>, stringable> table; 24 | 25 | auto make_graph = [](int V, double p, double alpha) { 26 | auto [g, s, t] = random_geometric_flow_dag_connected(V, p, alpha); 27 | int E = g.size(); 28 | auto cost = rands_wide(E, 1, 10'000'000, -1); 29 | auto cap = rands_wide(E, 1, 10'000'000, -1); 30 | return make_tuple(E, g, s, t, cost, cap); 31 | }; 32 | 33 | for (auto [V, pV, alpha] : inputs) { 34 | START_ACC(mcmf); 35 | double p = min(1.0, pV / V); 36 | int64_t Es = 0, Exp = pV * V; 37 | 38 | LOOP_FOR_DURATION_TRACKED_RUNS (runtime, now, runs) { 39 | print_time(now, runtime, "speed mcmf V={} E={} a={:.3f} ({} runs)", V, Exp, 40 | alpha, runs); 41 | 42 | auto [E, g, s, t, cost, cap] = make_graph(V, p, alpha); 43 | 44 | ADD_TIME_BLOCK(mcmf) { 45 | mcmflow g0(V); 46 | for (int i = 0; i < E; i++) { 47 | g0.add(g[i][0], g[i][1], cap[i], cost[i]); 48 | } 49 | g0.dag_init(s, t); 50 | g0.mincost_flow(s, t); 51 | } 52 | 53 | Es += E; 54 | } 55 | 56 | table[{{pV, alpha}, V, "mcmf"}] = FORMAT_EACH(mcmf, runs); 57 | table[{{pV, alpha}, V, "E"}] = format("{:.1f}", 1.0 * Es / runs); 58 | } 59 | 60 | print_time_table(table, "Mincost maxflow"); 61 | } 62 | 63 | int main() { 64 | RUN_BLOCK(speed_test_mincost_flow()); 65 | return 0; 66 | } 67 | -------------------------------------------------------------------------------- /code/numeric/kahan.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | using namespace std; 5 | 6 | template 7 | struct kahan { 8 | using value_type = D; 9 | D sum = 0, c = 0; 10 | 11 | explicit kahan(D value = 0, D comp = 0) : sum(value), c(comp) {} 12 | 13 | operator D() const noexcept { return sum; } 14 | 15 | friend bool operator==(kahan a, kahan b) { return a.sum == b.sum; } 16 | friend bool operator!=(kahan a, kahan b) { return a.sum != b.sum; } 17 | friend bool operator<(kahan a, kahan b) { return a.sum < b.sum; } 18 | friend bool operator>(kahan a, kahan b) { return a.sum > b.sum; } 19 | friend bool operator<=(kahan a, kahan b) { return a.sum <= b.sum; } 20 | friend bool operator>=(kahan a, kahan b) { return a.sum >= b.sum; } 21 | 22 | kahan& set(D x) { return sum = x, c = 0, *this; } 23 | 24 | friend kahan& operator+=(kahan& k, kahan add) { 25 | D y = add.sum + k.c - add.c, t = k.sum + y; 26 | k.c = (t - k.sum) - y, k.sum = t; 27 | return k; 28 | } 29 | friend kahan& operator-=(kahan& k, kahan sub) { 30 | D y = sub.sum - k.c - sub.c, t = k.sum - y; 31 | k.c = (t - k.sum) + y, k.sum = t; 32 | return k; 33 | } 34 | friend kahan& operator+=(kahan& k, D add) { 35 | D y = add + k.c, t = k.sum + y; 36 | k.c = (t - k.sum) - y, k.sum = t; 37 | return k; 38 | } 39 | friend kahan& operator-=(kahan& k, D sub) { 40 | D y = sub - k.c, t = k.sum - y; 41 | k.c = (t - k.sum) + y, k.sum = t; 42 | return k; 43 | } 44 | friend kahan& operator*=(kahan& k, D mul) { return k.sum *= mul, k.c *= mul, k; } 45 | friend kahan& operator/=(kahan& k, D div) { return k.sum /= div, k.c /= div, k; } 46 | friend kahan operator+(kahan k, D add) { return k += add; } 47 | friend kahan operator-(kahan k, D sub) { return k -= sub; } 48 | friend kahan operator*(kahan k, D mul) { return k *= mul; } 49 | friend kahan operator/(kahan k, D div) { return k /= div; } 50 | friend kahan operator+(kahan k) { return kahan(+k.sum, +k.c); } 51 | friend kahan operator-(kahan k) { return kahan(-k.sum, -k.c); } 52 | 53 | friend string to_string(kahan k) { return to_string(k.sum); } 54 | friend ostream& operator<<(ostream& out, kahan k) { return out << to_string(k.sum); } 55 | friend istream& operator>>(istream& in, kahan& k) { return in >> k.sum; } 56 | }; 57 | -------------------------------------------------------------------------------- /code/test/delaunay.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utils.hpp" 2 | #include "geometry/delaunay.hpp" 3 | #include "geometry/generator2d.hpp" 4 | 5 | void stress_test_delaunay() { 6 | LOOP_FOR_DURATION_TRACKED_RUNS (20s, now, runs) { 7 | print_time(now, 20s, "stress delaunay ({} runs)", runs); 8 | 9 | // Degenerate distributions too hard for floating points :( 10 | int N = rand_unif(4, 300); 11 | auto distr = rand_point_distribution(); 12 | auto pts = generate_points(N, distr, 0); 13 | 14 | auto data = delaunay(pts); 15 | auto faces = Wedge::extract_faces(data); 16 | vector> triangles; 17 | check_constrained_triangulation(faces, pts, {}); 18 | 19 | for (int i = 1, F = faces.size(); i < F; i++) { 20 | assert(faces[i].size() == 3u); 21 | triangles.push_back({faces[i][0], faces[i][1], faces[i][2]}); 22 | } 23 | 24 | for (auto [a, b, c] : triangles) { 25 | for (int i = 0; i < N; i++) { 26 | assert(!inside_circumference(pts[i], pts[a], pts[b], pts[c])); 27 | } 28 | assert(orientation(pts[a], pts[b], pts[c]) > 0); 29 | } 30 | Wedge::release(); 31 | } 32 | } 33 | 34 | void speed_test_delaunay() { 35 | vector Ns = {6000, 15000, 30000, 50000, 100000, 200000, 300000, 500000, 800000}; 36 | 37 | vector> inputs; 38 | for (int N : Ns) { 39 | for (int i = 0; i < int(PointDistrib::END); i++) { 40 | inputs.push_back({N, PointDistrib(i)}); 41 | } 42 | } 43 | 44 | const auto runtime = 180000ms / inputs.size(); 45 | map, string> table; 46 | 47 | for (auto [N, dist] : inputs) { 48 | START_ACC(delaunay); 49 | 50 | LOOP_FOR_DURATION_TRACKED_RUNS (runtime, now, runs) { 51 | print_time(now, runtime, "speed delaunay {} N={}", N, to_string(dist)); 52 | 53 | auto pts = generate_points(N, dist, 0, 10'000'000); 54 | 55 | ADD_TIME_BLOCK(delaunay) { 56 | delaunay(pts); 57 | Wedge::release(); 58 | } 59 | } 60 | 61 | table[{dist, N}] = FORMAT_EACH(delaunay, runs); 62 | } 63 | 64 | print_time_table(table, "Delaunay Triangulation"); 65 | } 66 | 67 | int main() { 68 | RUN_BLOCK(stress_test_delaunay()); 69 | RUN_BLOCK(speed_test_delaunay()); 70 | return 0; 71 | } 72 | -------------------------------------------------------------------------------- /code/matching/bipartite_matching.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | using namespace std; 5 | 6 | // Simple maximum bipartite matching O(VE), better for not huge matchings 7 | struct bipartite_matching { 8 | static inline default_random_engine rng = default_random_engine(random_device{}()); 9 | int U, V; 10 | vector> adj; 11 | vector mu, mv; 12 | 13 | bipartite_matching(int U, int V) : U(U), V(V), adj(U) { clear(); } 14 | 15 | void add(int u, int v) { 16 | assert(0 <= u && u < U && 0 <= v && v < V); 17 | adj[u].push_back(v); 18 | } 19 | 20 | void shuffle_edges() { 21 | for (int u = 0; u < U; u++) { 22 | shuffle(begin(adj[u]), end(adj[u]), rng); 23 | } 24 | } 25 | 26 | vector vis; 27 | int iteration = 0; 28 | 29 | // Run once, augment for u 30 | bool augment(int u) { 31 | iteration++; 32 | return dfs(u); 33 | } 34 | 35 | // Clear mating of u if it exists 36 | bool clear(int u) { 37 | if (mu[u] != -1) { 38 | mu[u] = mv[mu[u]] = -1; 39 | return true; 40 | } 41 | return false; 42 | } 43 | 44 | // Clear whole mating, reset vis 45 | void clear() { 46 | vis.assign(U, 0); 47 | mu.assign(U, -1); 48 | mv.assign(V, -1); 49 | iteration = 0; 50 | } 51 | 52 | bool dfs(int u) { 53 | vis[u] = iteration; 54 | for (int v : adj[u]) { 55 | if (mv[v] == -1) { 56 | mu[u] = v, mv[v] = u; 57 | return true; 58 | } 59 | } 60 | for (int v : adj[u]) { 61 | if (vis[mv[v]] != iteration && dfs(mv[v])) { 62 | mu[u] = v, mv[v] = u; 63 | return true; 64 | } 65 | } 66 | return false; 67 | } 68 | 69 | int optimize() { 70 | vector order(U); 71 | iota(begin(order), end(order), 0); 72 | shuffle(begin(order), end(order), rng); 73 | int new_mates = 0, mates = 0; 74 | do { 75 | iteration++, new_mates = 0; 76 | for (int u : order) { 77 | new_mates += mu[u] == -1 && dfs(u); 78 | } 79 | mates += new_mates; 80 | } while (new_mates && mates < U && mates < V); 81 | return mates; 82 | } 83 | 84 | int max_matching() { return clear(), optimize(); } 85 | }; 86 | -------------------------------------------------------------------------------- /code/test/primes.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utils.hpp" 2 | #include "numeric/sieves.hpp" 3 | #include "numeric/modnum.hpp" 4 | #include "numeric/math.hpp" 5 | #include "random.hpp" 6 | 7 | void stress_test_primitive_root() { 8 | constexpr int N = 3000; 9 | auto small_primes = classic_sieve(N); 10 | 11 | for (int p : small_primes) { 12 | int g = primitive_root_prime(p); 13 | vector seen(p + 1); 14 | seen[1] = true; 15 | long v = 1; 16 | for (int i = 1; i <= p - 2; i++) { 17 | v = v * g % p; 18 | assert(v != 1); 19 | seen[v] = true; 20 | } 21 | for (int i = 1; i <= p - 1; i++) { 22 | assert(seen[i]); 23 | } 24 | assert(!seen[p] && !seen[0]); 25 | } 26 | } 27 | 28 | void test_freq_primitive_root() { 29 | constexpr int N = 10'000'000; 30 | auto primes = classic_sieve(N); 31 | map freq; 32 | for (int p : primes) { 33 | freq[primitive_root_prime(p)]++; 34 | } 35 | debug(freq); 36 | } 37 | 38 | void stress_test_jacobi() { 39 | for (long n = 1; n < 300; n += 2) { 40 | for (long m = 1; m < 300; m += 2) { 41 | if (gcd(n, m) == 1) { 42 | int reciprocity = ((n % 4) == 3 && (m % 4) == 3) ? -1 : 1; 43 | assert(jacobi(n, m) * jacobi(m, n) == reciprocity); 44 | } 45 | } 46 | } 47 | } 48 | 49 | void stress_test_miller_rabin() { 50 | constexpr long N = 4'000'000; 51 | auto primes = classic_sieve(N); 52 | 53 | vector small_prime(N + 1, false); 54 | for (int p : primes) { 55 | small_prime[p] = true; 56 | } 57 | for (long n = 1; n <= N; n++) { 58 | assert(small_prime[n] == miller_rabin(n)); 59 | } 60 | 61 | for (int v : {5, 20, 300, 1000}) { 62 | long L = N * (N - v), R = N * (N - v + 5); 63 | auto large_primes = get_primes(L, R, primes); 64 | vector large_prime(R - L + 1, false); 65 | for (long n : large_primes) { 66 | large_prime[n - L] = true; 67 | } 68 | for (long n = L; n <= N; n++) { 69 | assert(large_prime[n - L] == miller_rabin(n)); 70 | } 71 | } 72 | } 73 | 74 | int main() { 75 | RUN_BLOCK(test_freq_primitive_root()); 76 | RUN_BLOCK(stress_test_primitive_root()); 77 | RUN_BLOCK(stress_test_jacobi()); 78 | RUN_BLOCK(stress_test_miller_rabin()); 79 | return 0; 80 | } 81 | -------------------------------------------------------------------------------- /code/test/halfplane.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utils.hpp" 2 | #include "geometry/halfplane.hpp" 3 | 4 | void run_halfplane_unit_test(const vector& hp, const string& header) { 5 | println("====== {} ({} halfplanes)", header, hp.size()); 6 | 7 | auto hull = halfplane_isect(hp); 8 | println("Lines:"); 9 | for (int i = 0, N = hp.size(); i < N; i++) { 10 | println("[{}]: {} + t{}", i, hp[i].p, hp[i].d); 11 | } 12 | 13 | println("Hull: {}", hull); 14 | } 15 | 16 | void unit_test_halfplane_isect() { 17 | vector hp; 18 | 19 | hp = {Ray::ray(Pt2(0, -10), Pt2(1, 0)), Ray::ray(Pt2(0, -15), Pt2(1, 1)), 20 | Ray::ray(Pt2(10, 0), Pt2(0, 1)), Ray::ray(Pt2(15, 0), Pt2(-1, 1)), 21 | Ray::ray(Pt2(0, 10), Pt2(-1, 0)), Ray::ray(Pt2(0, 15), Pt2(-1, -1)), 22 | Ray::ray(Pt2(-10, 0), Pt2(0, -1)), Ray::ray(Pt2(-15, 0), Pt2(1, -1))}; 23 | run_halfplane_unit_test(hp, "regular-octagon"); 24 | 25 | hp = {Ray::ray(Pt2(0, -10), Pt2(1, 0)), Ray::ray(Pt2(0, -15), Pt2(1, 1)), 26 | Ray::ray(Pt2(10, 0), Pt2(0, 1)), Ray::ray(Pt2(15, 0), Pt2(-1, 1)), 27 | Ray::ray(Pt2(0, 10), Pt2(-1, 0)), Ray::ray(Pt2(0, 15), Pt2(-1, -1)), 28 | Ray::ray(Pt2(-10, 0), Pt2(0, -1)), Ray::ray(Pt2(-15, 0), Pt2(1, -1)), 29 | Ray::ray(Pt2(0, -20), Pt2(1, 1)), Ray::ray(Pt2(20, 0), Pt2(-2, 1))}; 30 | run_halfplane_unit_test(hp, "octagon-cuts-3"); 31 | 32 | hp = {Ray::ray(Pt2(0, -10), Pt2(1, 0)), Ray::ray(Pt2(0, -15), Pt2(1, 1)), 33 | Ray::ray(Pt2(10, 0), Pt2(0, 1)), Ray::ray(Pt2(15, 0), Pt2(-1, 1)), 34 | Ray::ray(Pt2(0, 10), Pt2(-1, 0)), Ray::ray(Pt2(0, 15), Pt2(-1, -1)), 35 | Ray::ray(Pt2(-10, 0), Pt2(0, -1)), Ray::ray(Pt2(-15, 0), Pt2(1, -1)), 36 | Ray::ray(Pt2(0, -20), Pt2(1, 1)), Ray::ray(Pt2(21, 0), Pt2(-2, 1))}; 37 | run_halfplane_unit_test(hp, "octagon-wont-cut-3"); 38 | 39 | hp = {Ray::ray(Pt2(0, -10), Pt2(10, 1)), Ray::ray(Pt2(0, 10), Pt2(-10, 1)), 40 | Ray::ray(Pt2(10, 0), Pt2(1, -10)), Ray::ray(Pt2(-10, 0), Pt2(1, 10))}; 41 | run_halfplane_unit_test(hp, "half-out-cube"); 42 | 43 | hp = {Ray::ray(Pt2(0, 0), Pt2(1, 0)), Ray::ray(Pt2(10, 0), Pt2(3, 1)), 44 | Ray::ray(Pt2(-10, 0), Pt2(1, -1)), Ray::ray(Pt2(-20, 0), Pt2(1, -10)), 45 | Ray::ray(Pt2(-19, 0), Pt2(1, -3)), Ray::ray(Pt2(12, 0), Pt2(0, 1))}; 46 | run_halfplane_unit_test(hp, "parabola"); 47 | } 48 | 49 | int main() { 50 | RUN_BLOCK(unit_test_halfplane_isect()); 51 | return 0; 52 | } 53 | -------------------------------------------------------------------------------- /code/lib/general_matching.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "lib/graph_generator.hpp" 4 | 5 | /** 6 | * Generate a random Erdos graph with maximum matching M. 7 | * This algorithm is not perfect; not all topologies can be generated, I think, and some 8 | * topologies will appear more frequently due to symmetry. 9 | * 10 | * To generate a general graph on V vertices with maximum matching M. 11 | * First add edges (2n,2n+1) for n=0...M-1. This is the canonical matching. 12 | * Then add more edges as follows: 13 | * - Case 1: intend to add E-M more edges (to get E total): 14 | * - Do not add any edge (u,v) where u>=2M and v>=2M 15 | * - We will add some of the following edges: 16 | * - a: (2u,2v+1) where 0<=u(E - M, 2, {0, 0}, {A, B}); 37 | int a = s[0], b = s[1]; 38 | return array{a, b}; 39 | } 40 | 41 | auto general_matching_hide_topology(int V, edges_t& g) { 42 | shuffle(begin(g), end(g), mt); 43 | random_relabel_graph_inplace(V, g); 44 | random_flip_graph_inplace(g); 45 | } 46 | 47 | auto random_general_matching(int V, int M, int E) { 48 | auto [a, b] = general_matching_group_sizes(V, M, E); 49 | edges_t g; 50 | g.reserve(E); 51 | 52 | for (int n = 0; n < M; n++) 53 | g.push_back({2 * n, 2 * n + 1}); 54 | for (auto [u, v] : distinct_pair_sample(a, 0, M)) 55 | g.push_back({2 * u, 2 * v + 1}); 56 | for (auto [u, v] : pair_sample(b, 0, M, 2 * M, V)) 57 | g.push_back({2 * u, v}); 58 | 59 | return g; 60 | } 61 | 62 | auto random_general_matching(int V, int M, double p) { 63 | binomd distE(general_matching_max_edges(V, M), min(p, 1.0)); 64 | int E = max(M, int(distE(mt))); 65 | return random_general_matching(V, M, E); 66 | } 67 | -------------------------------------------------------------------------------- /code/test/optimization.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utils.hpp" 2 | #include "algo/optimization.hpp" 3 | 4 | template 5 | auto solve_1d1d_naive(int N, V zero, Dn&& dn, CostFn&& costfn) { 6 | static constexpr V inf = numeric_limits::max(); 7 | vector E(N, inf), D(N); 8 | E[0] = zero; 9 | D[0] = dn(E[0], 0); 10 | 11 | for (int i = 1; i < N; i++) { 12 | for (int j = 0; j < i; j++) { 13 | E[i] = min(E[i], D[j] + costfn(j, i)); 14 | } 15 | D[i] = dn(E[i], i); 16 | } 17 | 18 | return E; 19 | } 20 | 21 | void stress_test_solve_1d1d_concave() { 22 | LOOP_FOR_DURATION_OR_RUNS_TRACKED (5s, now, 100'000, runs) { 23 | print_time(now, 5s, "stress test 1d1d concave ({} runs)\r", runs); 24 | int N = rand_unif(1, 100); 25 | int A = rand_unif(-3, 0); 26 | int B = rand_unif(-100, 100); 27 | int C = rand_unif(-300, 300); 28 | auto d = rands_unif(N, -900, 900); 29 | auto costfn = [&](int j, int i) { 30 | return A * (i - j) * (i - j) + B * (i - j) + C; 31 | }; 32 | auto dn = [&](int64_t v, int i) { return v + d[i]; }; 33 | 34 | auto fast = solve_1d1d_concave(N, 0L, dn, costfn); 35 | auto naive = solve_1d1d_naive(N, 0L, dn, costfn); 36 | assert(naive == fast); 37 | } 38 | } 39 | 40 | void stress_test_solve_1d1d_convex() { 41 | LOOP_FOR_DURATION_OR_RUNS_TRACKED (5s, now, 100'000, runs) { 42 | print_time(now, 5s, "stress test 1d1d convex ({} runs)\r", runs); 43 | int N = rand_unif(1, 100); 44 | int A = rand_unif(0, 3); 45 | int B = rand_unif(-100, 100); 46 | int C = rand_unif(-300, 300); 47 | auto d = rands_unif(N, -900, 900); 48 | auto costfn = [&](int j, int i) { 49 | return A * (i - j) * (i - j) + B * (i - j) + C; 50 | }; 51 | auto dn = [&](int64_t v, int i) { return v + d[i]; }; 52 | 53 | auto fast = solve_1d1d_convex(N, 0L, dn, costfn); 54 | auto naive = solve_1d1d_naive(N, 0L, dn, costfn); 55 | assert(naive == fast); 56 | } 57 | } 58 | 59 | int main() { 60 | RUN_BLOCK(stress_test_solve_1d1d_concave()); 61 | RUN_BLOCK(stress_test_solve_1d1d_convex()); 62 | return 0; 63 | } 64 | 65 | // A(a-j)(a-j) + B(a-j) - A(b-j)(b-j) - B(b-j) 66 | // A(a^2 + j^2 - 2aj - b^2 -j^2 + 2bj) + B(a-b) 67 | // A(a^2 - b^2 - 2(a-b)j) + B(a-b) 68 | // A(a^2 - b^2) + (a-b)(Aj+B-2) 69 | // A(a^2 - b^2) + (a-b)(Aj+B-2) >= A(a^2 - b^2) + (a-b)(Ai+B-2) 70 | // (a-b)Aj >= (a-b)Ai true for A<=0 71 | -------------------------------------------------------------------------------- /code/test/string_search.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utils.hpp" 2 | #include "strings/boyer_moore.hpp" 3 | #include "strings/kmp.hpp" 4 | #include "strings/z_search.hpp" 5 | #include "lib/strings.hpp" 6 | 7 | auto naive_search_all(const string& haystack, const string& needle) { 8 | int P = needle.size(), T = haystack.size(); 9 | vector indices; 10 | const char* np = needle.data(); 11 | const char* hp = haystack.data(); 12 | for (int i = 0; i + P <= T; i++) { 13 | if (equal(np, np + P, hp + i)) 14 | indices.push_back(i); 15 | } 16 | return indices; 17 | } 18 | 19 | void stress_test_string_searchers() { 20 | vector needles, haystacks; 21 | 22 | auto add = [&](vector& strs, vector&& more) { 23 | strs.insert(end(strs), begin(more), end(more)); 24 | }; 25 | 26 | add(needles, generate_all_strings(1, 6, 'a', 'c')); 27 | add(needles, {"abab", "ababa", "ababab", "abababab", "abba", "abbaab", "abbaabba"}); 28 | add(needles, {"cabab", "abcabababc", "ababcabcab", "ababcab", "abcabab"}); 29 | add(needles, {"aabaa", "aaaab", "baaaa", "ababa", "bcabcaabc"}); 30 | add(needles, {"accbcc", "accbccacbc", "accbccaccbcc", "acbcacbc"}); 31 | add(needles, {".", ",!?#:", "aa.bb", "a,", ",a"}); 32 | add(needles, rand_strings(37, 7, 15, 'a', 'c')); 33 | add(needles, rand_strings(20, 16, 40, 'a', 'c')); 34 | 35 | add(haystacks, {"...", "#?!", "?!", "!?", " ", "abcdabc", ",bab,", ",aba,"}); 36 | add(haystacks, generate_all_strings(0, 7, 'a', 'c')); 37 | add(haystacks, rand_strings(3'000, 8, 15, 'a', 'c')); 38 | add(haystacks, rand_strings(1'000, 16, 70, 'a', 'c')); 39 | add(haystacks, rand_strings(100, 71, 1000, 'a', 'c')); 40 | add(haystacks, rand_strings(20, 1001, 10000, 'a', 'c')); 41 | 42 | int N = needles.size(), H = haystacks.size(); 43 | print(" no. needles: {}\n", N); 44 | print(" no. haystacks: {}\n", H); 45 | 46 | for (int i = 0; i < N; i++) { 47 | print_progress(i, N, "stress test string searchers"); 48 | auto needle = needles[i]; 49 | KMP kmp(needle); 50 | BoyerMoore bm(needle); 51 | for (auto haystack : haystacks) { 52 | auto i1 = naive_search_all(haystack, needle); 53 | auto i2 = kmp_search_all(haystack, kmp); 54 | auto i3 = boyer_moore_search_all(haystack, bm); 55 | auto i4 = z_search_all(haystack, needle); 56 | assert(i1 == i2); 57 | assert(i1 == i3); 58 | assert(i1 == i4); 59 | } 60 | } 61 | } 62 | 63 | int main() { 64 | RUN_BLOCK(stress_test_string_searchers()); 65 | return 0; 66 | } 67 | -------------------------------------------------------------------------------- /code/geometry/all_point_pairs.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "geometry/geometry2d.hpp" 4 | 5 | /** 6 | * visit every pair of points, with all points sorted outline. 7 | * radial sweep ccw around the origin, starting at -Oy and ending at +Oy 8 | * there must be no coincident points. Collinear points are ok. 9 | * Signature: 10 | * processor(const order&, int i, int u, int v) 11 | * where we visit the pair (pts[u],pts[v]) with order[i] == u and all 12 | * other points are sorted along the direction RH(pts[u],pts[v]) 13 | */ 14 | template 15 | void all_point_pairs_radial_sweep(const vector& pts, Fn&& processor) { 16 | int N = pts.size(); 17 | assert(N <= 10'000); // sanity check - we need O(N^2) memory 18 | 19 | vector order(N); 20 | iota(begin(order), end(order), 0); 21 | sort(begin(order), end(order), [&](int u, int v) { return pts[u] < pts[v]; }); 22 | 23 | vector rank(N); 24 | for (int i = 0; i < N; i++) { 25 | rank[order[i]] = i; 26 | } 27 | 28 | int S = N * (N - 1) / 2; 29 | vector> slopes(S); 30 | for (int k = 0, i = 0; i < N; i++) { 31 | for (int j = i + 1; j < N; j++, k++) { 32 | slopes[k] = {order[i], order[j]}; 33 | } 34 | } 35 | sort(begin(slopes), end(slopes), [&](const auto& u, const auto& v) { 36 | const auto &a = pts[u[0]], b = pts[u[1]], c = pts[v[0]], d = pts[v[1]]; 37 | if (auto cuv = cross(b - a, d - c)) { 38 | return cuv > 0; // different slopes 39 | } else if (a != c) { 40 | return make_pair(a.x, a.y) < make_pair(c.x, c.y); 41 | } else { 42 | return make_pair(b.x, b.y) < make_pair(d.x, d.y); 43 | } 44 | }); 45 | 46 | for (int i = 0; i < S; i++) { 47 | auto [u, v] = slopes[i]; 48 | int a = rank[u], b = rank[v]; 49 | if (a > b) { 50 | swap(a, b); 51 | swap(u, v); 52 | } 53 | assert(a + 1 == b); 54 | processor(order, a, u, v); 55 | swap(order[a], order[b]); 56 | swap(rank[u], rank[v]); 57 | } 58 | } 59 | 60 | auto smallest_triangle_area(const vector& pts) { 61 | Pt2::L ans = numeric_limits::max(); 62 | all_point_pairs_radial_sweep(pts, [&](const auto& order, int i, int u, int v) { 63 | int N = pts.size(); 64 | assert(order[i] == u); 65 | if (i > 0) { 66 | ans = min(ans, abs(cross(pts[order[i - 1]], pts[u], pts[v]))); 67 | } 68 | if (i + 1 < N) { 69 | ans = min(ans, abs(cross(pts[order[i + 1]], pts[u], pts[v]))); 70 | } 71 | }); 72 | return ans; 73 | } 74 | -------------------------------------------------------------------------------- /code/matching/hopcroft_karp.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | using namespace std; 5 | 6 | // Hopcroft-Karp maximum bipartite matching O(E√V), better for very large matchings 7 | struct hopcroft_karp { 8 | int U, V; 9 | vector> adj; 10 | vector mu, mv; 11 | 12 | hopcroft_karp(int U, int V) : U(U), V(V), adj(U) {} 13 | 14 | void add(int u, int v) { 15 | assert(0 <= u && u < U && 0 <= v && v < V); 16 | adj[u].push_back(v); 17 | } 18 | 19 | vector vis, dist, bfs; 20 | int iteration; 21 | static inline constexpr int inf = INT_MAX / 2; 22 | 23 | bool run_bfs() { 24 | int S = 0; 25 | for (int u = 0; u < U; u++) { 26 | if (mu[u] == -1) { 27 | dist[u] = 0; 28 | bfs[S++] = u; 29 | } else { 30 | dist[u] = inf; 31 | } 32 | } 33 | dist[U] = inf; 34 | for (int i = 0; i < S; i++) { 35 | if (int u = bfs[i]; dist[u] < dist[U]) { 36 | for (int v : adj[u]) { 37 | // note: the check v != mu[u] is implicit in dist[mv[v]] == inf 38 | if (dist[mv[v]] == inf) { 39 | dist[mv[v]] = dist[u] + 1; 40 | bfs[S++] = mv[v]; 41 | } 42 | } 43 | } 44 | } 45 | return dist[U] != inf; 46 | } 47 | 48 | bool dfs(int u) { 49 | if (u == U) { 50 | return true; 51 | } 52 | if (vis[u] == iteration) { 53 | return false; 54 | } 55 | vis[u] = iteration; 56 | for (int v : adj[u]) { 57 | if (dist[mv[v]] == dist[u] + 1 && dfs(mv[v])) { 58 | mv[v] = u; 59 | mu[u] = v; 60 | return true; 61 | } 62 | } 63 | return false; 64 | } 65 | 66 | int max_matching() { 67 | vis.assign(U + 1, 0); 68 | dist.assign(U + 1, 0); 69 | bfs.assign(U + 1, 0); 70 | mu.assign(U + 1, -1); 71 | mv.assign(V, U); 72 | int mates = 0; 73 | iteration = 0; 74 | static mt19937 rng(random_device{}()); 75 | for (int u = 0; u < U; u++) { 76 | shuffle(begin(adj[u]), end(adj[u]), rng); 77 | } 78 | while (run_bfs() && mates < U && mates < V) { 79 | iteration++; 80 | for (int u = 0; u < U; u++) { 81 | if (mu[u] == -1 && dfs(u)) { 82 | mates++; 83 | } 84 | } 85 | } 86 | mu.pop_back(); 87 | return mates; 88 | } 89 | }; 90 | -------------------------------------------------------------------------------- /code/graphs/shallow_decomposition.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "algo/y_combinator.hpp" 4 | 5 | inline int lowbits(int u) { return u == 0 ? 0 : 8 * sizeof(int) - __builtin_clz(u); } 6 | 7 | // Build shallowest decomposition of tree/forest. O(n) 8 | auto build_shallow_decomposition(const vector>& tree) { 9 | int V = tree.size(); 10 | vector dp(V), parent(V, -1), label(V); 11 | vector> stacks(lowbits(V + 1)); 12 | 13 | auto greedy_labeling = y_combinator([&](auto self, int u, int p) -> void { 14 | int seen = 0, twice = 0; 15 | for (int v : tree[u]) { 16 | if (v != p) { 17 | self(v, u); 18 | twice |= seen & dp[v]; 19 | seen |= dp[v]; 20 | } 21 | } 22 | int fix = (1 << lowbits(twice)) - 1; 23 | dp[u] = 1 + (seen | fix); 24 | label[u] = __builtin_ctz(dp[u]); 25 | }); 26 | 27 | auto create_chain = [&](int labels, int u) { 28 | while (labels) { 29 | int label = lowbits(labels) - 1; 30 | labels ^= 1 << label; 31 | int v = stacks[label].top(); 32 | stacks[label].pop(); 33 | parent[v] = u, u = v; 34 | } 35 | }; 36 | 37 | auto decompose = y_combinator([&](auto self, int u, int p, int D) -> void { 38 | int T = tree[u].size(); 39 | for (int i = 0; i < T; i++) { 40 | if (int v = tree[u][i]; v != p) { 41 | self(v, u, D); 42 | } 43 | } 44 | stacks[label[u]].push(u); 45 | for (int i = T - 1; i >= 0; i--) { 46 | if (int v = tree[u][i]; v != p) { 47 | create_chain(dp[v] & ~dp[u], u); 48 | } 49 | } 50 | }); 51 | 52 | for (int u = 0; u < V; u++) { 53 | if (dp[u] == 0) { 54 | greedy_labeling(u, -1); 55 | int D = lowbits(dp[u]) - 1; 56 | decompose(u, -1, D); 57 | int root = stacks[D].top(); 58 | stacks[D].pop(); 59 | create_chain(dp[u] & ~dp[root], root); 60 | } 61 | } 62 | 63 | // Compute depths on the decomposition 64 | vector depth(V); 65 | stack path; 66 | 67 | for (int u = 0; u < V; u++) { 68 | int v = parent[u]; 69 | while (v != -1 && depth[v] == 0) { 70 | path.push(v), v = parent[v]; 71 | } 72 | while (!path.empty()) { 73 | v = path.top(), path.pop(); 74 | depth[v] = parent[v] == -1 ? 0 : 1 + depth[parent[v]]; 75 | } 76 | depth[u] = parent[u] == -1 ? 0 : 1 + depth[parent[u]]; 77 | } 78 | 79 | return make_tuple(move(parent), move(depth), move(label)); 80 | } 81 | -------------------------------------------------------------------------------- /code/parallel/graph_orchestrator.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "parallel/priority_thread_pool.hpp" // priority_thread_pool 4 | #include "parallel/spinlock.hpp" // concurrent_queue 5 | 6 | /** 7 | * Check fn_orchestrator.hpp for an explanation of what an orchestrator does 8 | * 9 | * The second orchestrator version uses an explicit graph. The amount of work done in 10 | * the the main thread is O(E) locks/unlocks, where E is the number of edges in the 11 | * graph. Cycles are allowed to exist: nodes in cycles are simply ignored. 12 | */ 13 | struct graph_orchestrator { 14 | private: 15 | int N, E; 16 | vector adj, off, deps; 17 | 18 | auto toposort() const { 19 | vector cnt(N, 0), dfs; 20 | int j = 0, S = 0; 21 | 22 | for (int u = 0; u < N; u++) 23 | if (deps[u] == 0) 24 | dfs.push_back(u), S++; 25 | 26 | while (j < S) { 27 | int u = dfs[j++]; 28 | for (int i = off[u]; i < off[u + 1]; i++) 29 | if (int v = adj[i]; ++cnt[v] == deps[v]) 30 | dfs.push_back(v), S++; 31 | } 32 | 33 | return dfs; 34 | } 35 | 36 | public: 37 | explicit graph_orchestrator(int N, const vector>& g) 38 | : N(N), E(g.size()), adj(E), off(N + 1, 0), deps(N, 0) { 39 | for (auto [u, v] : g) 40 | off[u + 1]++, deps[v]++; 41 | partial_sum(begin(off), end(off), begin(off)); 42 | auto cur = off; 43 | for (auto [u, v] : g) 44 | adj[cur[u]++] = v; 45 | } 46 | 47 | bool verify() const { return int(toposort().size()) == N; } 48 | 49 | template 50 | void sequential_make(const Fn& job) { 51 | for (int u : toposort()) 52 | job(u); 53 | } 54 | 55 | template 56 | void concurrent_make(const Fn& job, int nthreads) { 57 | vector cnt(N, 0); 58 | priority_thread_pool pool(nthreads); 59 | concurrent_queue done(N); 60 | int seen = 0; 61 | 62 | auto runner = [&job, &done](int u) { job(u), done.push(u); }; 63 | 64 | for (int u = 0; u < N; u++) 65 | if (deps[u] == 0) 66 | pool.submit(off[u + 1] - off[u], runner, u), seen++; 67 | 68 | while (seen < N) { 69 | pool.wait_for(1); 70 | while (!done.empty()) { 71 | int u = done.pop(); 72 | for (int i = off[u]; i < off[u + 1]; i++) { 73 | int v = adj[i]; 74 | if (++cnt[v] == deps[v]) 75 | pool.submit(off[v + 1] - off[v], runner, v), seen++; 76 | } 77 | } 78 | } 79 | 80 | pool.finish(); 81 | } 82 | }; 83 | -------------------------------------------------------------------------------- /code/graphs/dominator_tree.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "algo/y_combinator.hpp" 4 | 5 | /** 6 | * Build dominator tree of given graph 7 | * Nodes 1-indexed (out[0] empty) 8 | * Source: 9 | * "A Fast Algorithm for Finding Dominators in a Flow Graph", T. Lengauer, R. Tarjan 10 | * https://tanujkhattar.wordpress.com/2016/01/11/dominator-tree-of-a-directed-graph/ 11 | */ 12 | auto build_dominator_tree(int root, const vector>& out) { 13 | int V = out.size(); 14 | assert(V > 0 && root > 0 && root < V); 15 | 16 | vector> in(V); 17 | vector dom(V), parent(V), semi(V), vertex(V), ancestor(V), label(V); 18 | iota(begin(label), end(label), 0); 19 | int timer = 1; 20 | 21 | for (int u = 1; u < V; u++) { 22 | for (int v : out[u]) { 23 | in[v].push_back(u); 24 | } 25 | } 26 | 27 | auto dfs_index = y_combinator([&](auto self, int u) -> void { 28 | vertex[timer] = u, semi[u] = timer++; 29 | for (int v : out[u]) { 30 | if (semi[v] == 0) { 31 | parent[v] = u; 32 | self(v); 33 | } 34 | } 35 | }); 36 | 37 | auto compress = y_combinator([&](auto self, int v) -> void { 38 | if (ancestor[ancestor[v]] != 0) { 39 | self(ancestor[v]); 40 | if (semi[label[v]] > semi[label[ancestor[v]]]) { 41 | label[v] = label[ancestor[v]]; 42 | } 43 | ancestor[v] = ancestor[ancestor[v]]; 44 | } 45 | }); 46 | 47 | auto eval = [&](int v) -> int { 48 | if (ancestor[v] == 0) { 49 | return v; 50 | } else { 51 | compress(v); 52 | return label[v]; 53 | } 54 | }; 55 | 56 | dfs_index(root); 57 | 58 | vector bucket_head(V, 0), bucket_next(V, 0); 59 | 60 | for (int i = V - 1; i >= 2; i--) { 61 | int w = vertex[i]; 62 | for (int v : in[w]) { 63 | int u = eval(v); 64 | semi[w] = min(semi[w], semi[u]); 65 | } 66 | // push w onto the front of bucket b 67 | int b = vertex[semi[w]]; 68 | bucket_next[w] = bucket_head[b], bucket_head[b] = w; 69 | ancestor[w] = parent[w]; // link 70 | // visit all nodes in bucket parent[w] 71 | for (int v = bucket_head[parent[w]]; v != 0; v = bucket_next[v]) { 72 | int u = eval(v); 73 | dom[v] = semi[u] < semi[v] ? u : parent[w]; 74 | } 75 | bucket_head[parent[w]] = 0; 76 | } 77 | 78 | for (int i = 2; i < V; i++) { 79 | int w = vertex[i]; 80 | if (dom[w] != vertex[semi[w]]) { 81 | dom[w] = dom[dom[w]]; 82 | } 83 | } 84 | dom[root] = 0; 85 | 86 | // might wish to return semi as well 87 | return make_pair(dom, parent); 88 | } 89 | -------------------------------------------------------------------------------- /code/test/centroid_forest.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utils.hpp" 2 | #include "struct/centroid_forest.hpp" 3 | #include "struct/shallow_forest.hpp" 4 | #include "lib/graph_generator.hpp" 5 | #include "lib/graph_formats.hpp" 6 | 7 | void speed_test_centroid_forest() { 8 | vector Ns = {10'000, 40'000, 100'000, 300'000, 600'000, 1'000'000}; 9 | vector Rs = {10'000, 40'000, 100'000, 300'000, 600'000, 1'000'000}; 10 | 11 | vector> inputs; 12 | for (int N : Ns) { 13 | for (int R : Rs) { 14 | inputs.emplace_back(N, R); 15 | } 16 | } 17 | map, string>, stringable> table; 18 | 19 | for (auto [N, R] : inputs) { 20 | printcl("speed centroid/shallow forest N={} R={}", N, R); 21 | START_ACC3(ctd, ctd_build, ctd_query); 22 | START_ACC3(shw, shw_build, shw_query); 23 | 24 | auto g = random_geometric_tree(N, .02); 25 | vector> tree(N); 26 | for (auto [u, v] : g) { 27 | tree[u].push_back(v), tree[v].push_back(u); 28 | } 29 | 30 | vector arr = rands_unif(N, -500, 500); 31 | vector> queries(R); 32 | vector a(R), b(R); 33 | 34 | for (int i = 0; i < R; i++) { 35 | queries[i] = diff_unif(0, N - 1); 36 | } 37 | 38 | ADD_TIME_BLOCK(ctd) { 39 | START(ctd_build); 40 | centroid_disjoint_sparse_table ctd(tree, arr, plus{}); 41 | ADD_TIME(ctd_build); 42 | 43 | START(ctd_query); 44 | for (int i = 0; i < R; i++) { 45 | auto [u, v] = queries[i]; 46 | a[i] = ctd.query(u, v); 47 | } 48 | ADD_TIME(ctd_query); 49 | } 50 | 51 | ADD_TIME_BLOCK(shw) { 52 | START(shw_build); 53 | shallow_disjoint_sparse_table shw(tree, arr, plus{}); 54 | ADD_TIME(shw_build); 55 | 56 | START(shw_query); 57 | for (int i = 0; i < R; i++) { 58 | auto [u, v] = queries[i]; 59 | b[i] = shw.query(u, v); 60 | } 61 | ADD_TIME(shw_query); 62 | } 63 | 64 | // Same responses to the queries 65 | assert(a == b); 66 | 67 | table[{{N, R}, "ctd"}] = FORMAT_TIME(ctd); 68 | table[{{N, R}, "ctd_build"}] = FORMAT_TIME(ctd_build); 69 | table[{{N, R}, "ctd_query"}] = FORMAT_TIME(ctd_query); 70 | table[{{N, R}, "shw"}] = FORMAT_TIME(shw); 71 | table[{{N, R}, "shw_build"}] = FORMAT_TIME(shw_build); 72 | table[{{N, R}, "shw_query"}] = FORMAT_TIME(shw_query); 73 | } 74 | 75 | print_time_table(table, "Centroid vs shallow forest"); 76 | } 77 | 78 | int main() { 79 | RUN_BLOCK(speed_test_centroid_forest()); 80 | return 0; 81 | } 82 | -------------------------------------------------------------------------------- /code/struct/persistent_deque.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "struct/persistent_queue.hpp" 4 | 5 | /** 6 | * Persistent dueue with random access from the front or back in O(log N) time 7 | * Reference: https://publications.mpi-cbg.de/Myers_1983_6328.pdf 8 | * Note: cannot modify version 0 inplace (empty version) 9 | */ 10 | template 11 | struct persistent_jump_deque { 12 | persistent_jump_queue fq, bq; // front queue and back queue 13 | 14 | persistent_jump_deque() = default; 15 | 16 | bool empty(int v) const { return fq.empty(v) && bq.empty(v); } 17 | int versions() const { return fq.versions(); } 18 | int size(int v) const { return fq.size(v) + bq.size(v); } 19 | 20 | T& front(int v) { return fq.empty(v) ? bq.front(v) : fq.back(v); } 21 | T& back(int v) { return bq.empty(v) ? fq.front(v) : bq.back(v); } 22 | 23 | int new_empty() { return fq.new_empty(), bq.new_empty(); } 24 | 25 | int clone(int v) { return fq.clone(v), bq.clone(v); } 26 | 27 | int pop_front(int v) { 28 | if (fq.empty(v)) { 29 | return fq.clone(v), bq.pop(v); 30 | } else { 31 | return bq.clone(v), fq.pop_back(v); 32 | } 33 | } 34 | 35 | int pop_back(int v) { 36 | if (bq.empty(v)) { 37 | return bq.clone(v), fq.pop(v); 38 | } else { 39 | return fq.clone(v), bq.pop_back(v); 40 | } 41 | } 42 | 43 | T& pop_front_inplace(int v) { 44 | if (fq.empty(v)) { 45 | return bq.pop_inplace(v); 46 | } else { 47 | return fq.pop_back_inplace(v); 48 | } 49 | } 50 | 51 | T& pop_back_inplace(int v) { 52 | if (bq.empty(v)) { 53 | return fq.pop_inplace(v); 54 | } else { 55 | return bq.pop_back_inplace(v); 56 | } 57 | } 58 | 59 | int push_front(int v, T element) { return bq.clone(v), fq.push(v, move(element)); } 60 | 61 | int push_back(int v, T element) { return fq.clone(v), bq.push(v, move(element)); } 62 | 63 | void push_front_inplace(int v, T element) { 64 | return fq.push_inplace(v, move(element)); 65 | } 66 | 67 | void push_back_inplace(int v, T element) { 68 | return bq.push_inplace(v, move(element)); // 69 | } 70 | 71 | // 0 <= k < size(v), k=0 will give the front of the deque 72 | T& find_from_front(int v, int k) { 73 | if (int l = fq.size(v); k < l) { 74 | return fq.find_from_back(v, k); 75 | } else { 76 | return bq.find_from_front(v, k - l); 77 | } 78 | } 79 | 80 | // 0 <= k < size(v), k=0 will give the back of the deque 81 | T& find_from_back(int v, int k) { 82 | if (int l = bq.size(v); k < l) { 83 | return bq.find_from_back(v, k); 84 | } else { 85 | return fq.find_from_front(v, k - l); 86 | } 87 | } 88 | }; 89 | -------------------------------------------------------------------------------- /code/test/slopy.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utils.hpp" 2 | #include "../struct/slopy.hpp" 3 | 4 | void unit_test_slopy() { 5 | auto test_slopes = [&](const vector>& slopes) { 6 | Slopy fn; 7 | 8 | for (auto [s, len] : slopes) { 9 | Branch::insert_segment(fn.tree, s, len); 10 | } 11 | 12 | vector value = {0}; 13 | for (auto [s, len] : slopes) { 14 | while (len--) { 15 | value.push_back(value.back() + s); 16 | } 17 | } 18 | int V = value.size(); 19 | int A = *min_element(begin(value), end(value)); 20 | int B = *max_element(begin(value), end(value)); 21 | 22 | for (int ceiling = A - 3; ceiling <= B + 3; ceiling++) { 23 | auto [l, r] = Branch::ceiling_range(fn.tree, ceiling); 24 | int L = V, R = 0; 25 | for (int i = 0; i < V; i++) { 26 | if (value[i] <= ceiling) { 27 | L = min(i, L); 28 | R = max(i, R); 29 | } 30 | } 31 | if (ceiling < A) { 32 | assert(l == -1 && r == -1); 33 | } else { 34 | assert(L == l && R == r); 35 | } 36 | } 37 | 38 | assert(V >= 12); 39 | int R = V - 1; 40 | 41 | for (int i = 0; i < V; i++) { 42 | assert(fn.query(i) == value[i]); 43 | } 44 | fn.trimto(5, R - 5); 45 | for (int i = 5; i <= R - 5; i++) { 46 | assert(fn.query(i) == value[i]); 47 | } 48 | }; 49 | 50 | test_slopes({ 51 | {-6, 10}, // 52 | {-5, 3}, // 53 | {-4, 11}, // 54 | {-2, 13}, // 55 | {-1, 4}, // 56 | {0, 9}, // 57 | {2, 5}, // 58 | {5, 6}, // 59 | {7, 6}, // 60 | {12, 11}, // 61 | }); 62 | 63 | test_slopes({ 64 | {-6, 10}, // 65 | {-5, 3}, // 66 | {2, 5}, // 67 | {7, 6}, // 68 | {12, 11}, // 69 | }); 70 | 71 | test_slopes({ 72 | {0, 9}, // 73 | {2, 5}, // 74 | {5, 6}, // 75 | {7, 6}, // 76 | {12, 11}, // 77 | }); 78 | 79 | test_slopes({ 80 | {-6, 10}, // 81 | {-5, 3}, // 82 | {-4, 11}, // 83 | {-2, 13}, // 84 | {-1, 4}, // 85 | {0, 9}, // 86 | }); 87 | 88 | test_slopes({ 89 | {2, 5}, // 90 | {5, 6}, // 91 | {7, 6}, // 92 | {12, 11}, // 93 | }); 94 | 95 | test_slopes({ 96 | {-6, 10}, // 97 | {-5, 3}, // 98 | {-4, 11}, // 99 | {-2, 13}, // 100 | {-1, 4}, // 101 | }); 102 | } 103 | 104 | int main() { 105 | RUN_BLOCK(unit_test_slopy()); 106 | return 0; // 107 | } 108 | -------------------------------------------------------------------------------- /code/test/tensor.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utils.hpp" 2 | #include "struct/tensor.hpp" 3 | #include "lib/anynum.hpp" 4 | 5 | template 6 | using mat = vector>; 7 | 8 | template 9 | auto multiply_tensors(const tensor& a, const tensor& b) { 10 | auto [N, M] = a.size(); 11 | auto [Z, K] = b.size(); 12 | assert(M == Z); 13 | tensor c({N, K}, 0); 14 | for (int i = 0; i < N; i++) { 15 | for (int j = 0; j < M; j++) { 16 | for (int k = 0; k < K; k++) { 17 | c[{i, k}] += a[{i, j}] * b[{j, k}]; 18 | } 19 | } 20 | } 21 | return c; 22 | } 23 | 24 | template 25 | auto multiply_mats(const mat& a, const mat& b) { 26 | int N = a.size(), M = a[0].size(), K = b[0].size(); 27 | mat c(N, vector(K)); 28 | for (int i = 0; i < N; i++) { 29 | for (int j = 0; j < M; j++) { 30 | for (int k = 0; k < K; k++) { 31 | c[i][k] += a[i][j] * b[j][k]; 32 | } 33 | } 34 | } 35 | return c; 36 | } 37 | 38 | template 39 | auto generate_mat(int N, int M, int v = 30) { 40 | mat arr(N, vector(M)); 41 | for (int i = 0; i < N; i++) { 42 | for (int j = 0; j < M; j++) { 43 | arr[i][j] = uniform_gen(-v, v); 44 | } 45 | } 46 | return arr; 47 | } 48 | 49 | template 50 | auto convert_to_tensor(const vector>& arr) { 51 | int N = arr.size(), M = arr[0].size(); 52 | tensor t({N, M}); 53 | for (int i = 0; i < N; i++) { 54 | for (int j = 0; j < M; j++) { 55 | t[{i, j}] = arr[i][j]; 56 | } 57 | } 58 | return t; 59 | } 60 | 61 | void speed_test_tensor_multiply() { 62 | START_ACC2(mat, tensor); 63 | 64 | LOOP_FOR_DURATION_OR_RUNS_TRACKED (5s, now, 100'000, runs) { 65 | print_time(now, 5s, "stress test tensor x vvi"); 66 | 67 | int N = rand_unif(100, 200); 68 | int M = rand_unif(100, 200); 69 | int K = rand_unif(100, 200); 70 | mat amat = generate_mat(N, M, 1000000); 71 | mat bmat = generate_mat(M, K, 1000000); 72 | tensor aten = convert_to_tensor(amat); 73 | tensor bten = convert_to_tensor(bmat); 74 | 75 | START(mat); 76 | auto cmat = multiply_mats(amat, bmat); 77 | ADD_TIME(mat); 78 | 79 | START(tensor); 80 | auto cten = multiply_tensors(aten, bten); 81 | ADD_TIME(tensor); 82 | 83 | for (int i = 0; i < N; i++) { 84 | for (int j = 0; j < K; j++) { 85 | assert(cmat[i][j] == (cten[{i, j}])); 86 | } 87 | } 88 | } 89 | 90 | PRINT_EACH(mat, runs); 91 | PRINT_EACH(tensor, runs); 92 | } 93 | 94 | int main() { 95 | RUN_BLOCK(speed_test_tensor_multiply()); 96 | return 0; 97 | } 98 | -------------------------------------------------------------------------------- /code/test/compress.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utils.hpp" 2 | #include "struct/pbds.hpp" 3 | 4 | void speed_test_compression() { 5 | vector Ns = {3000, 50000, 100000, 200000, 300000, 500000, 1000000, 2000000}; 6 | 7 | map, stringable> times; 8 | 9 | for (int N : Ns) { 10 | START_ACC3(sort, map, lower_bound); 11 | START_ACC3(inline, unordered_map, gp_hash_table); 12 | 13 | vector nums = rands_unif(N, -1e9, 1e9); 14 | vector a = nums, b = nums, c = nums, d = nums, e(N); 15 | 16 | ADD_TIME_BLOCK(inline) { 17 | vector> x(N); 18 | for (int i = 0; i < N; i++) { 19 | x[i] = {nums[i], i}; 20 | } 21 | sort(begin(x), end(x)); 22 | e[x[0].second] = 0; 23 | for (int i = 1; i < N; i++) { 24 | e[x[i].second] = e[x[i - 1].second] + (x[i].first != x[i - 1].first); 25 | } 26 | } 27 | 28 | ADD_TIME_BLOCK(sort) { 29 | sort(begin(nums), end(nums)); 30 | nums.erase(unique(begin(nums), end(nums)), end(nums)); 31 | } 32 | 33 | int M = nums.size(); 34 | 35 | ADD_TIME_BLOCK(lower_bound) { 36 | for (int i = 0; i < N; i++) { 37 | a[i] = lower_bound(begin(nums), end(nums), a[i]) - begin(nums); 38 | } 39 | } 40 | 41 | ADD_TIME_BLOCK(map) { 42 | map mm; 43 | for (int i = 0; i < M; i++) { 44 | mm.emplace_hint(mm.end(), nums[i], i); 45 | } 46 | for (int i = 0; i < N; i++) { 47 | b[i] = mm.at(b[i]); 48 | } 49 | } 50 | 51 | ADD_TIME_BLOCK(unordered_map) { 52 | unordered_map mm; 53 | mm.reserve(N); 54 | for (int i = 0; i < M; i++) { 55 | mm.emplace(nums[i], i); 56 | } 57 | for (int i = 0; i < N; i++) { 58 | c[i] = mm.at(c[i]); 59 | } 60 | } 61 | 62 | ADD_TIME_BLOCK(gp_hash_table) { 63 | hash_map mm; 64 | for (int i = 0; i < M; i++) { 65 | mm[nums[i]] = i; 66 | } 67 | for (int i = 0; i < N; i++) { 68 | d[i] = mm[d[i]]; 69 | } 70 | } 71 | 72 | assert(a == b && a == c && a == d && a == e); 73 | 74 | times[{"sort", N}] = FORMAT_TIME(sort); 75 | times[{"map", N}] = FORMAT_TIME(map); 76 | times[{"inline", N}] = FORMAT_TIME(inline); 77 | times[{"lower_bound", N}] = FORMAT_TIME(lower_bound); 78 | times[{"unordered_map", N}] = FORMAT_TIME(unordered_map); 79 | times[{"gp_hash_table", N}] = FORMAT_TIME(gp_hash_table); 80 | } 81 | 82 | print_time_table(times, "Coordinate compression (-1e9..1e9)"); 83 | } 84 | 85 | int main() { 86 | RUN_BLOCK(speed_test_compression()); 87 | return 0; 88 | } 89 | -------------------------------------------------------------------------------- /code/test/strings.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utils.hpp" 2 | #include "strings/strings.hpp" 3 | 4 | void unit_test_good_suffix() { 5 | string ss[] = { 6 | "addbddcdd", // 1 2 3 4 5 6 7 8 9 10 7 | // a d d b d d c d d 8 | // . . . . . . 3 1 2 9 | "ABBABAB", // A B B A B A B 10 | // 5 6 4 5 6 7 7 8 11 | // . . . 2 . 4 1 12 | "ccacc", // c c a c c 13 | // . . . 1 2 14 | "CABAB", // C A B A B 15 | // 5 3 4 5 5 6 16 | // . . 2 . 1 17 | "ABCABBACCABCAB", // A B C A B B A C C A B C A B 18 | // x x x x x x x x x y 3 y 8 1, x=9, y=12 19 | }; 20 | for (const string& s : ss) { 21 | print(" border [{}]: {}\n", s, build_border_function(s)); 22 | print(" true/good [{}]: {}\n", s, build_good_suffix_border_table(s, true).first); 23 | print("false/good [{}]: {}\n", s, build_good_suffix_border_table(s, false).first); 24 | } 25 | } 26 | 27 | void unit_test_manachers() { 28 | string ss[] = { 29 | "abcdfdcecba"s, 30 | "abababc"s, 31 | "abaabbbaaaacca"s, 32 | }; 33 | for (string& s : ss) { 34 | cout << build_manachers(s) << endl; 35 | } 36 | } 37 | 38 | void unit_test_prefix_function() { 39 | string ss[] = { 40 | "abcabcd"s, // 0,0,0,1,2,3,0 41 | "aabaaab"s, // 0,1,0,1,2,2,3 42 | }; 43 | for (string& s : ss) { 44 | cout << build_prefix_function(s) << endl; 45 | } 46 | } 47 | 48 | void unit_test_z_function() { 49 | string ss[] = { 50 | "aaaaa"s, // 0,4,3,2,1 51 | "aaabaab"s, // 0,2,1,0,2,1,0 52 | "abacaba"s, // 0,0,1,0,3,0,1 53 | }; 54 | for (string& s : ss) { 55 | cout << build_z_function(s) << endl; 56 | } 57 | } 58 | 59 | void unit_test_lyndon() { 60 | string ss[] = { 61 | "efdcba"s, // ef d c b a | 0,2,3,4,5 62 | "abaabaaabaaaab"s, // ab aab aaab aaaab | 0,2,5,9 63 | "abacaba"s, // abac ab a | 0,4,6 64 | }; 65 | for (string& s : ss) { 66 | cout << build_lyndon_factorization(s) << endl; 67 | } 68 | } 69 | 70 | void unit_test_suffix_array() { 71 | using vi = vector; 72 | string s; 73 | vi sa, lcp; 74 | 75 | s = "abbaacaaabab"s; 76 | sa = build_cyclic_shifts(s); 77 | lcp = build_lcp_array(s, sa); 78 | 79 | for (int i = 0, N = s.size(); i < N; i++) { 80 | print("{:2}: {}\n", i, s.substr(sa[i]) + s.substr(0, sa[i])); 81 | } 82 | print(" sa: {}\nlcp: {}\n", sa, lcp); 83 | } 84 | 85 | int main() { 86 | RUN_SHORT(unit_test_manachers()); 87 | RUN_SHORT(unit_test_prefix_function()); 88 | RUN_SHORT(unit_test_z_function()); 89 | RUN_SHORT(unit_test_lyndon()); 90 | RUN_SHORT(unit_test_good_suffix()); 91 | RUN_SHORT(unit_test_suffix_array()); 92 | return 0; 93 | } 94 | -------------------------------------------------------------------------------- /code/geometry/halfplane.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "geometry/geometry2d.hpp" 4 | 5 | // Compute intersection of halfplanes as ordered list of halfplanes. O(n log n). 6 | // All halfplanes point left (aka ccw of direction). Returns {} if intersection is empty. 7 | // Suppresses null area contributors. In particular, returns nothing if the area is 0. 8 | auto halfplane_isect(const vector& hp) { 9 | int N = hp.size(); 10 | vector index(N); 11 | iota(begin(index), end(index), 0); 12 | sort(begin(index), end(index), 13 | [&](int i, int j) { return angle_sort(hp[i].d, hp[j].d); }); 14 | 15 | // Filter consecutive halfplanes with the same direction, taking the most restrictive 16 | vector relevant; 17 | for (int i = 0, R = relevant.size(); i < N; i++) { 18 | int u = index[i], v = R ? relevant.back() : -1; 19 | if (R == 0 || cross(hp[u].d, hp[v].d) || dot(hp[u].d, hp[v].d) <= 0) { 20 | relevant.push_back(u), R++; 21 | } else if (cross(hp[u].d, hp[v].p - hp[u].p) < 0) { 22 | relevant[R - 1] = u; 23 | } 24 | } 25 | 26 | auto empty_isect = [&](int u, int v, int w) { 27 | auto cuv = cross(hp[u].d, hp[v].d); 28 | auto cuw = cross(hp[u].d, hp[w].d); 29 | auto cvw = cross(hp[v].d, hp[w].d); 30 | return cuw < 0 ? cuv > 0 && cvw >= 0 && !hp[u].isect_compare_unsafe(hp[w], hp[v]) 31 | : cuw <= 0 && cross(hp[u].d, hp[w].p - hp[u].p) <= 0; 32 | }; 33 | 34 | auto redundant = [&](int u, int v, int w) { // v is redundant between u and w 35 | return cross(hp[u].d, hp[w].d) > 0 && !hp[u].isect_compare_unsafe(hp[v], hp[w]); 36 | }; 37 | 38 | deque hull; 39 | int S = 0; 40 | 41 | for (int u : relevant) { 42 | while (S > 1) { 43 | if (redundant(hull[S - 2], hull[S - 1], u)) { 44 | hull.pop_back(), S--; 45 | } else if (redundant(u, hull[0], hull[1])) { 46 | hull.pop_front(), S--; 47 | } else if (empty_isect(hull[S - 2], hull[S - 1], u)) { 48 | return deque(); 49 | } else if (empty_isect(u, hull[0], hull[1])) { 50 | return deque(); 51 | } else { 52 | break; 53 | } 54 | } 55 | if (S <= 1 || !redundant(hull[S - 1], u, hull[0])) { 56 | hull.push_back(u), S++; 57 | } 58 | } 59 | 60 | while (S > 2) { 61 | if (redundant(hull[S - 2], hull[S - 1], hull[0])) { 62 | hull.pop_back(), S--; 63 | } else if (redundant(hull[S - 1], hull[0], hull[1])) { 64 | hull.pop_front(), S--; 65 | } else if (empty_isect(hull[S - 2], hull[S - 1], hull[0])) { 66 | return deque(); 67 | } else if (empty_isect(hull[S - 1], hull[0], hull[1])) { 68 | return deque(); 69 | } else { 70 | break; 71 | } 72 | } 73 | 74 | return hull; 75 | } 76 | -------------------------------------------------------------------------------- /code/lib/test_progress.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "lib/test_chrono.hpp" 5 | #include "formatting.hpp" 6 | 7 | bool cout_is_terminal() { 8 | static int is = -1; 9 | return is < 0 ? (is = isatty(STDOUT_FILENO)) : is; 10 | } 11 | 12 | auto& log_destination() { return cout_is_terminal() ? std::cout : std::cerr; } 13 | 14 | void clear_line() { cout_is_terminal() ? fmt::print("\r\033[2K") : (void)fflush(stdout); } 15 | 16 | template 17 | void printcl(Ts&&... args) { 18 | auto& dest = log_destination(); 19 | fmt::print(dest, "\r\033[2K"); 20 | fmt::print(dest, forward(args)...); 21 | flush(dest); 22 | } 23 | 24 | template 25 | void putcln(Ts&&... args) { 26 | auto& dest = log_destination(); 27 | fmt::print(dest, "\r\003[2K"); 28 | putln(dest, forward(args)...); 29 | flush(dest); 30 | } 31 | 32 | void print_progress(long i, long N) { 33 | double percent = 100.0 * (i + 1) / N; 34 | int digits = to_string(N).size(); 35 | printcl("{:5.1f}% {:>{}}/{}", percent, i + 1, digits, N); 36 | } 37 | 38 | template 39 | void print_progress(long i, long N, T&& content) { 40 | double percent = 100.0 * (i + 1) / N; 41 | int digits = to_string(N).size(); 42 | string txt = fmt::format("{}", forward(content)); 43 | printcl("{:5.1f}% {:>{}}/{} {}", percent, i + 1, digits, N, txt); 44 | } 45 | 46 | template 47 | void print_progress(long i, long N, string_view fmt, Ts&&... args) { 48 | return print_progress(i, N, fmt::format(fmt, forward(args)...)); 49 | } 50 | 51 | template 52 | void print_regular(long i, long N, long step, Ts&&... args) { 53 | if ((i == 0 || (i + 1) % step == 0)) { 54 | print_progress(i, N, forward(args)...); 55 | } 56 | } 57 | 58 | template 59 | void print_time_view(T1 now, T2 duration, T&& content) { 60 | double percent = 100.0 * now / duration; 61 | auto txt = fmt::format("{}", forward(content)); 62 | printcl("{:5.1f}% {}", percent, txt); 63 | } 64 | 65 | template 66 | void print_time_view(T1 now, T2 duration, string_view fmt, Ts&&... args) { 67 | double percent = 100.0 * now / duration; 68 | auto txt = fmt::format(fmt, forward(args)...); 69 | printcl("{:5.1f}% {}", percent, txt); 70 | } 71 | 72 | template 73 | void print_time(T1 now, T2 duration, Ts&&... args) { 74 | static const chrono::milliseconds step = 0ms; 75 | static chrono::nanoseconds next_now = 30ns; 76 | if ((sizeof...(Ts) > 1 || now == 0ns || now >= next_now)) { 77 | next_now = now == 0ns ? step : now + step; 78 | print_time_view(now, duration, forward(args)...); 79 | } 80 | } 81 | 82 | template 83 | [[noreturn]] void fail(Ts&&... args) { 84 | fmt::print(log_destination(), "\n"), clear_line(); 85 | fmt::print(log_destination(), "Error: {}", fmt::format(forward(args)...)); 86 | exit(1); 87 | } 88 | -------------------------------------------------------------------------------- /code/algo/mos.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "algo/y_combinator.hpp" 4 | 5 | // Sort queries {l,r} for mo decomposition in the universe [0,N) with block size B 6 | auto mosort_left_block(int N, const vector>& queries, int B = 0) { 7 | int Q = queries.size(); 8 | if (B == 0 && Q > 0) { 9 | B = max(1, N / sqrt(Q)); 10 | } 11 | vector> block(Q); 12 | for (int i = 0; i < Q; i++) { 13 | auto [x, y] = queries[i]; 14 | assert(0 <= x && x <= y && y <= N); 15 | block[i].first = x / B; 16 | block[i].second = (block[i].first & 1) ? -y : y; 17 | } // O(Q) divisions instead of O(Q log Q) 18 | vector order(Q); 19 | iota(begin(order), end(order), 0); 20 | sort(begin(order), end(order), [&](int i, int j) { return block[i] < block[j]; }); 21 | return order; 22 | } 23 | 24 | // Index of (x,y) along hilbert curve of size nxn power of two 25 | int64_t xy2d_hilbert(int n, int x, int y) { 26 | int64_t d = 0; 27 | for (int64_t s = n / 2; s > 0; s >>= 1) { 28 | int rx = (x & s) > 0; 29 | int ry = (y & s) > 0; 30 | d += s * s * ((3 * ry) ^ rx); 31 | if (rx == 0) { 32 | if (ry == 1) { 33 | x = s - 1 - x; 34 | y = s - 1 - y; 35 | } 36 | swap(x, y); 37 | } 38 | } 39 | return d; 40 | } 41 | 42 | // Sort queries {l,r} for mo decomposition in the universe [0,N) with hilbert curve order 43 | auto mosort_hilbert_curve(int N, const vector>& queries) { 44 | int K = N > 1 ? 8 * sizeof(int) - __builtin_clz(N - 1) : 0; 45 | int Q = queries.size(); 46 | vector ord(Q); 47 | for (int i = 0; i < Q; i++) { 48 | ord[i] = xy2d_hilbert(1 << K, queries[i][0], queries[i][1]); 49 | } 50 | vector order(Q); 51 | iota(begin(order), end(order), 0); 52 | sort(begin(order), end(order), [&](int i, int j) { return ord[i] < ord[j]; }); 53 | return order; 54 | } 55 | 56 | auto offline_range_distinct(const vector& a, const vector>& queries) { 57 | int N = a.size(), Q = queries.size(); 58 | auto order = mosort_left_block(N, queries); 59 | vector ans(Q), pre(N), nxt(N); 60 | map vals; 61 | for (int i = 0; i < N; i++) { 62 | int j = vals.count(a[i]) ? vals.at(a[i]) : -1; 63 | pre[i] = j, vals[a[i]] = i; 64 | } 65 | vals.clear(); 66 | for (int i = N - 1; i >= 0; i--) { 67 | int j = vals.count(a[i]) ? vals.at(a[i]) : N; 68 | nxt[i] = j, vals[a[i]] = i; 69 | } 70 | vals.clear(); 71 | int L = 0, R = 0, cnt = 0; 72 | for (int q : order) { 73 | auto [l, r] = queries[q]; 74 | while (L > l) { 75 | cnt += nxt[--L] >= R; 76 | } 77 | while (R < r) { 78 | cnt += pre[R++] < L; 79 | } 80 | while (L < l) { 81 | cnt -= nxt[L++] >= R; 82 | } 83 | while (R > r) { 84 | cnt -= pre[--R] < L; 85 | } 86 | ans[q] = cnt; 87 | } 88 | return ans; 89 | } 90 | -------------------------------------------------------------------------------- /code/numeric/chrono.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | using namespace std; 5 | 6 | using ns = chrono::nanoseconds; 7 | using us = chrono::microseconds; 8 | using ms = chrono::milliseconds; 9 | 10 | #define CONCAT(x, y) x##y 11 | 12 | #define LOOP_FOR_DURATION_IMPL(z, duration) \ 13 | auto CONCAT(loop_start, z) = chrono::steady_clock::now(); \ 14 | while (chrono::steady_clock::now() - CONCAT(loop_start, z) < duration) 15 | 16 | #define LOOP_FOR_DURATION_OR_RUNS_IMPL(z, duration, max_runs) \ 17 | auto CONCAT(loop_start, z) = chrono::steady_clock::now(); \ 18 | for (auto loop_counter_##z = 0; \ 19 | loop_counter_##z < max_runs && \ 20 | chrono::steady_clock::now() - CONCAT(loop_start, z) < duration; \ 21 | loop_counter_##z++) 22 | 23 | #define LOOP_FOR_DURATION_TRACKED_IMPL(z, duration, now) \ 24 | auto CONCAT(loop_start, z) = chrono::steady_clock::now(); \ 25 | for (auto now = decltype(chrono::steady_clock::now() - CONCAT(loop_start, z))(0); \ 26 | now < duration; now = chrono::steady_clock::now() - CONCAT(loop_start, z)) 27 | 28 | #define LOOP_FOR_DURATION_TRACKED_RUNS_IMPL(z, duration, now, runs) \ 29 | auto runs = 0; \ 30 | auto CONCAT(loop_start, z) = chrono::steady_clock::now(); \ 31 | for (auto now = decltype(chrono::steady_clock::now() - CONCAT(loop_start, z))(0); \ 32 | now < duration; \ 33 | now = chrono::steady_clock::now() - CONCAT(loop_start, z), runs++) 34 | 35 | #define LOOP_FOR_DURATION_OR_RUNS_TRACKED_IMPL(z, duration, now, max_runs, runs) \ 36 | auto runs = 0; \ 37 | auto CONCAT(loop_start, z) = chrono::steady_clock::now(); \ 38 | for (auto now = decltype(chrono::steady_clock::now() - CONCAT(loop_start, z))(0); \ 39 | runs < max_runs && now < duration; \ 40 | now = chrono::steady_clock::now() - CONCAT(loop_start, z), runs++) 41 | 42 | #define LOOP_FOR_DURATION(in_duration) LOOP_FOR_DURATION_IMPL(__COUNTER__, in_duration) 43 | 44 | #define LOOP_FOR_DURATION_OR_RUNS(in_duration, in_max_runs) \ 45 | LOOP_FOR_DURATION_OR_RUNS_IMPL(__COUNTER__, in_duration, in_max_runs) 46 | 47 | #define LOOP_FOR_DURATION_TRACKED(in_duration, out_now) \ 48 | LOOP_FOR_DURATION_TRACKED_IMPL(__COUNTER__, in_duration, out_now) 49 | 50 | #define LOOP_FOR_DURATION_TRACKED_RUNS(in_duration, out_now, out_runs) \ 51 | LOOP_FOR_DURATION_TRACKED_RUNS_IMPL(__COUNTER__, in_duration, out_now, out_runs) 52 | 53 | #define LOOP_FOR_DURATION_OR_RUNS_TRACKED(in_duration, out_now, in_max_runs, out_runs) \ 54 | LOOP_FOR_DURATION_OR_RUNS_TRACKED_IMPL(__COUNTER__, in_duration, out_now, \ 55 | in_max_runs, out_runs) 56 | -------------------------------------------------------------------------------- /code/test/bipartite_matching.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utils.hpp" 2 | #include "matching/bipartite_matching.hpp" 3 | #include "lib/bipartite_matching.hpp" 4 | #include "matching/hopcroft_karp.hpp" 5 | 6 | void stress_test_bipartite_matching() { 7 | LOOP_FOR_DURATION_OR_RUNS_TRACKED (20s, now, 1000, runs) { 8 | print_time(now, 20s, "stress bipartite matching ({} runs)", runs); 9 | 10 | int U = rand_unif(90, 600); 11 | int V = rand_unif(90, 600); 12 | int M = rand_unif(1, min(U, V)); 13 | double p = rand_unif(3, 30) / max(U, V); 14 | auto g = random_bipartite_matching(U, V, M, p); 15 | bipartite_matching_hide_topology(U, V, g); 16 | 17 | bipartite_matching mm(U, V); 18 | for (auto [u, v] : g) 19 | mm.add(u, v); 20 | mm.shuffle_edges(); 21 | 22 | int m0 = mm.max_matching(); 23 | 24 | hopcroft_karp hk(U, V); 25 | for (auto [u, v] : g) 26 | hk.add(u, v); 27 | int m1 = hk.max_matching(); 28 | 29 | assert(M == m0 && M == m1); 30 | } 31 | } 32 | 33 | void speed_test_bipartite_matching() { 34 | vector Us = {6000, 15000, 30000, 50000, 100000, 200000, 300000, 500000, 800000}; 35 | vector Vs = {6000, 15000, 30000, 50000, 100000, 200000, 300000, 500000, 800000}; 36 | vector pVs = {2.0, 5.0, 8.0, 12.0, 20.0}; 37 | 38 | vector> inputs; 39 | for (int U : Us) { 40 | for (int V : Vs) { 41 | for (double pV : pVs) { 42 | double p = pV / U, E = U * pV; 43 | if (p <= 1.0 && E <= 5'000'000) { 44 | inputs.push_back({U, V, pV}); 45 | } 46 | } 47 | } 48 | } 49 | 50 | const auto runtime = 360'000ms / inputs.size(); 51 | map, int, string>, string> table; 52 | 53 | for (auto [U, V, pV] : inputs) { 54 | START_ACC2(mm, hop); 55 | int E = U * pV; 56 | 57 | LOOP_FOR_DURATION_OR_RUNS_TRACKED (runtime, now, 1000, runs) { 58 | print_time(now, runtime, "speed bipartite matching U={} V={} E={} ({} runs)", 59 | U, V, E, runs); 60 | 61 | auto g = random_exact_bipartite(U, V, E); 62 | shuffle(begin(g), end(g), mt); 63 | 64 | ADD_TIME_BLOCK(mm) { 65 | bipartite_matching mm(U, V); 66 | for (auto [u, v] : g) 67 | mm.add(u, v); 68 | mm.max_matching(); 69 | } 70 | 71 | ADD_TIME_BLOCK(hop) { 72 | hopcroft_karp hk(U, V); 73 | for (auto [u, v] : g) 74 | hk.add(u, v); 75 | hk.max_matching(); 76 | } 77 | } 78 | 79 | table[{{V, pV}, U, "mm"}] = FORMAT_EACH(mm, runs); 80 | table[{{V, pV}, U, "hop"}] = FORMAT_EACH(hop, runs); 81 | } 82 | 83 | print_time_table(table, "Bipartite matching"); 84 | } 85 | 86 | int main() { 87 | RUN_BLOCK(stress_test_bipartite_matching()); 88 | RUN_BLOCK(speed_test_bipartite_matching()); 89 | return 0; 90 | } 91 | -------------------------------------------------------------------------------- /code/graphs/euler_tour.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "algo/y_combinator.hpp" 4 | 5 | /** 6 | * Compute an euler tour (cycle or path) of a directed or undirected graph 7 | * If all nodes have even degree it computes a cycle, with first edges starting at s. 8 | * If two nodes have odd degree it computes a path, with first edges starting at s. 9 | * The edges are u->{v,id} 10 | * Complexity: O(V + E) 11 | */ 12 | auto build_euler_tour(int s, int E, const vector>>& adj) { 13 | int V = adj.size(); 14 | vector used(E, false); 15 | vector cur(V, 0), path; 16 | 17 | auto dfs = y_combinator([&](auto self, int u) -> void { 18 | int deg = adj[u].size(); 19 | while (cur[u] != deg) { 20 | auto [v, e] = adj[u][cur[u]++]; 21 | if (!used[e]) { 22 | used[e] = true; 23 | self(v); 24 | path.push_back(e); 25 | } 26 | } 27 | }); 28 | 29 | dfs(s); 30 | assert(int(path.size()) == E); 31 | reverse(begin(path), end(path)); 32 | return path; 33 | } 34 | 35 | auto build_directed_euler_tour(int s, const vector>& edges) { 36 | vector> adj; 37 | for (int e = 0, E = edges.size(); e < E; e++) { 38 | auto [u, v] = edges[e]; 39 | adj.resize(max(int(adj.size()), 1 + max(u, v))); 40 | adj[u].push_back(e); 41 | } 42 | 43 | int V = adj.size(), E = edges.size(); 44 | vector used(E, false); 45 | vector cur(V, 0), path; 46 | 47 | auto dfs = y_combinator([&](auto self, int u) -> void { 48 | int deg = adj[u].size(); 49 | while (cur[u] != deg) { 50 | int e = adj[u][cur[u]++]; 51 | int v = edges[e][1]; 52 | if (!used[e]) { 53 | used[e] = true; 54 | self(v); 55 | path.push_back(e); 56 | } 57 | } 58 | }); 59 | 60 | dfs(s); 61 | assert(int(path.size()) == E); 62 | reverse(begin(path), end(path)); 63 | return path; 64 | } 65 | 66 | auto build_undirected_euler_tour(int s, const vector>& edges) { 67 | vector> adj; 68 | for (int e = 0, E = edges.size(); e < E; e++) { 69 | auto [u, v] = edges[e]; 70 | adj.resize(max(int(adj.size()), 1 + max(u, v))); 71 | adj[u].push_back(e); 72 | adj[v].push_back(e); 73 | } 74 | 75 | int V = adj.size(), E = edges.size(); 76 | vector used(E, false); 77 | vector cur(V, 0), path; 78 | // vector> path; 79 | 80 | auto dfs = y_combinator([&](auto self, int u) -> void { 81 | int deg = adj[u].size(); 82 | while (cur[u] != deg) { 83 | int e = adj[u][cur[u]++]; 84 | int v = edges[e][u == edges[e][0]]; 85 | if (!used[e]) { 86 | used[e] = true; 87 | self(v); 88 | path.push_back(e); 89 | // path.emplace_back(u, e); 90 | } 91 | } 92 | }); 93 | 94 | dfs(s); 95 | assert(int(path.size()) == E); 96 | reverse(begin(path), end(path)); 97 | return path; 98 | } 99 | -------------------------------------------------------------------------------- /code/numeric/rangenum.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | using namespace std; 5 | 6 | template 7 | struct rangenum { 8 | static_assert(is_integral::value); 9 | array n = {}; 10 | 11 | rangenum() = default; 12 | rangenum(array ab) : n(ab) {} 13 | rangenum(T v) : n({v, v}) {} 14 | rangenum(T a, T b) : n({a, b}) {} 15 | 16 | bool overlaps(rangenum x) const { return n[0] <= x[1] && x[0] <= n[1]; } 17 | bool contains(rangenum x) const { return n[0] <= x[0] && x[1] <= n[1]; } 18 | bool empty() const { return n[0] > n[1]; } 19 | T& operator[](int x) { return n[x]; } 20 | const T& operator[](int x) const { return n[x]; } 21 | 22 | rangenum rev() const { return rangenum(n[1], n[0]); } 23 | rangenum& reversed() { return *this = rev(); } 24 | 25 | rangenum operator-() const { return rangenum(-n[0], -n[1]); } 26 | rangenum operator+() const { return rangenum(n[0], n[1]); } 27 | rangenum operator++(int) const { return rangenum(n[0]++, n[1]++); } 28 | rangenum operator--(int) const { return rangenum(n[0]--, n[1]--); } 29 | rangenum& operator++() const { return ++n[0], ++n[1], *this; } 30 | rangenum& operator--() const { return --n[0], --n[1], *this; } 31 | rangenum& operator+=(rangenum v) { return n[0] += v[0], n[1] += v[1], *this; } 32 | rangenum& operator-=(rangenum v) { return n[0] -= v[0], n[1] -= v[1], *this; } 33 | rangenum& operator*=(rangenum v) { return n[0] *= v[0], n[1] *= v[1], *this; } 34 | rangenum& operator/=(rangenum v) { return n[0] /= v[0], n[1] /= v[1], *this; } 35 | rangenum& operator&=(rangenum v) { 36 | return n[0] = max(n[0], v[0]), n[1] = min(n[1], v[1]), *this; 37 | } 38 | rangenum& operator|=(rangenum v) { 39 | return n[0] = min(n[0], v[0]), n[1] = max(n[1], v[1]), *this; 40 | } 41 | 42 | friend rangenum operator+(rangenum lhs, rangenum rhs) { return lhs += rhs; } 43 | friend rangenum operator-(rangenum lhs, rangenum rhs) { return lhs -= rhs; } 44 | friend rangenum operator*(rangenum lhs, rangenum rhs) { return lhs *= rhs; } 45 | friend rangenum operator/(rangenum lhs, rangenum rhs) { return lhs /= rhs; } 46 | friend rangenum operator&(rangenum lhs, rangenum rhs) { return lhs &= rhs; } 47 | friend rangenum operator|(rangenum lhs, rangenum rhs) { return lhs |= rhs; } 48 | 49 | friend string to_string(const rangenum& v) { 50 | return "(" + to_string(v[0]) + "," + to_string(v[1]) + ")"; 51 | } 52 | friend bool operator==(rangenum lhs, rangenum rhs) { return lhs.n == rhs.n; } 53 | friend bool operator!=(rangenum lhs, rangenum rhs) { return lhs.n != rhs.n; } 54 | friend bool operator<(rangenum lhs, rangenum rhs) { return lhs.n < rhs.n; } 55 | friend bool operator>(rangenum lhs, rangenum rhs) { return lhs.n > rhs.n; } 56 | friend bool operator<=(rangenum lhs, rangenum rhs) { return lhs.n <= rhs.n; } 57 | friend bool operator>=(rangenum lhs, rangenum rhs) { return lhs.n >= rhs.n; } 58 | friend ostream& operator<<(ostream& out, rangenum v) { return out << to_string(v); } 59 | friend istream& operator>>(istream& in, rangenum& v) { return in >> v[0] >> v[1]; } 60 | }; 61 | -------------------------------------------------------------------------------- /code/algo/optimization.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | using namespace std; 5 | 6 | /** 7 | * E[i] = min{j= cost(i,a) - cost(i,b) 10 | * cost(i,b) - cost(j,b) >= cost(i,a) - cost(j,a) for j < i < b < a. O(N log N) 11 | */ 12 | template 13 | auto solve_1d1d_concave(int N, V zero, Dn&& dn, Cn&& costfn) { 14 | vector E(N), D(N); 15 | E[0] = zero; 16 | D[0] = dn(E[0], 0); 17 | 18 | vector> stk; 19 | 20 | auto cost = [&](int j, int i) { return D[j] + costfn(j, i); }; 21 | 22 | auto improv = [&](int j, int i, int t) { 23 | int l = i - 1, r = t; 24 | while (l + 1 < r) { 25 | int m = (l + r) / 2; 26 | cost(j, m) >= cost(i - 1, m) ? l = m : r = m; 27 | } 28 | return l; 29 | }; 30 | 31 | for (int i = 1, S = -1; i < N; i++) { 32 | while (S >= 0) { 33 | auto [j, t] = stk[S]; 34 | if (cost(i - 1, t) <= cost(j, t)) { 35 | stk.pop_back(), S--; 36 | } else { 37 | break; 38 | } 39 | } 40 | 41 | if (int t = S < 0 ? N - 1 : improv(stk[S][0], i, stk[S][1]); t >= i) { 42 | stk.push_back({i - 1, t}), S++; 43 | } 44 | 45 | E[i] = cost(stk[S][0], i); 46 | D[i] = dn(E[i], i); 47 | 48 | if (S >= 0 && stk[S][1] == i) { 49 | stk.pop_back(), S--; 50 | } 51 | } 52 | 53 | return E; 54 | } 55 | 56 | /** 57 | * E[i] = min{j 63 | auto solve_1d1d_convex(int N, V zero, Dn&& dn, Cn&& costfn) { 64 | vector E(N), D(N); 65 | E[0] = zero; 66 | D[0] = dn(E[0], 0); 67 | 68 | vector> deq; 69 | 70 | auto cost = [&](int j, int i) { return D[j] + costfn(j, i); }; 71 | 72 | auto improv = [&](int j, int i, int t) { 73 | int l = t, r = N; 74 | while (l + 1 < r) { 75 | int m = (l + r) / 2; 76 | cost(j, m) < cost(i - 1, m) ? l = m : r = m; 77 | } 78 | return r; 79 | }; 80 | 81 | for (int i = 1, S = -1, L = 0; i < N; i++) { 82 | while (S >= L) { 83 | auto [j, t] = deq[S]; 84 | if (cost(i - 1, t) <= cost(j, t)) { 85 | deq.pop_back(), S--; 86 | } else { 87 | break; 88 | } 89 | } 90 | 91 | if (int t = S < L ? i : improv(deq[S][0], i, deq[S][1]); t < N) { 92 | deq.push_back({i - 1, t}), S++; 93 | } 94 | 95 | E[i] = cost(deq[L][0], i); 96 | D[i] = dn(E[i], i); 97 | 98 | ++deq[L][1]; 99 | L += L < S && deq[L][1] == deq[L + 1][1]; 100 | } 101 | 102 | return E; 103 | } 104 | -------------------------------------------------------------------------------- /code/test/depth_first_tree.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utils.hpp" 2 | #include "struct/depth_first_tree.hpp" 3 | #include "lib/tree_action.hpp" 4 | 5 | using namespace tree_testing; 6 | 7 | void stress_test_depth_first_tree() { 8 | static actions_t stress_actions = { 9 | {RootedAT::LINK, 1500}, // 10 | {RootedAT::CUT, 300}, // 11 | {RootedAT::LINK_CUT, 1200}, // 12 | {RootedAT::FINDROOT, 1000}, // 13 | // {RootedAT::LCA, 500}, // 14 | // {RootedAT::LCA_CONN, 1000}, // 15 | 16 | {RootedAT::UPDATE_NODE, 1500}, // 17 | {RootedAT::QUERY_NODE, 2000}, // 18 | 19 | // {RootedAT::UPDATE_PATH, 3500}, // 20 | // {RootedAT::QUERY_PATH, 3500}, // 21 | // {RootedAT::PATH_LENGTH, 1500}, // 22 | 23 | {RootedAT::UPDATE_SUBTREE, 3500}, // 24 | {RootedAT::QUERY_SUBTREE, 3500}, // 25 | {RootedAT::SUBTREE_SIZE, 1500}, // 26 | 27 | // {RootedAT::UPDATE_TREE, 2500}, // 28 | // {RootedAT::QUERY_TREE, 2500}, // 29 | // {RootedAT::TREE_SIZE, 1000}, // 30 | }; 31 | 32 | const int N = 300; 33 | slow_tree slow(N); 34 | depth_first_tree> tree(N); 35 | auto actions = make_rooted_actions(N, 2s, stress_actions, 9 * N / 10); 36 | bool ok = true; 37 | 38 | for (int ia = 0, A = actions.size(); ia < A; ia++) { 39 | print_regular(ia, A, 1000, "stress test dft tree"); 40 | auto [action, u, v, r, who, val] = actions[ia]; 41 | 42 | switch (action) { 43 | case RootedAT::LINK: { 44 | slow.link(u, v); 45 | tree.link(u, v); 46 | } break; 47 | case RootedAT::CUT: { 48 | slow.cut(u); 49 | tree.cut(u); 50 | } break; 51 | case RootedAT::FINDROOT: { 52 | int ans = tree.findroot(u); 53 | ok = ans == who; 54 | } break; 55 | case RootedAT::QUERY_NODE: { 56 | long ans = tree.access_node(u)->self; 57 | ok = val == ans; 58 | } break; 59 | case RootedAT::UPDATE_NODE: { 60 | slow.update_node(u, val); 61 | tree.access_node(u)->self = val; 62 | } break; 63 | case RootedAT::UPDATE_SUBTREE: { 64 | slow.update_subtree(u, val); 65 | tree.access_subtree(u)->add_subtree(val); 66 | tree.end_access(); 67 | } break; 68 | case RootedAT::QUERY_SUBTREE: { 69 | long ans = tree.access_subtree(u)->subtree(); 70 | tree.end_access(); 71 | ok = val == ans; 72 | } break; 73 | case RootedAT::SUBTREE_SIZE: { 74 | long ans = tree.access_subtree(u)->subtree_size(); 75 | tree.end_access(); 76 | ok = val == ans; 77 | } break; 78 | default: 79 | throw runtime_error("Unsupported action"); 80 | } 81 | if (!ok) { 82 | printcl("Failed action: {}\n", action_names.at(action)); 83 | } 84 | assert(ok); 85 | } 86 | 87 | assert(ok); 88 | } 89 | 90 | int main() { 91 | RUN_BLOCK(stress_test_depth_first_tree()); 92 | return 0; 93 | } 94 | -------------------------------------------------------------------------------- /code/linear/matrix_cache.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "numeric/modnum.hpp" 4 | 5 | namespace std { 6 | 7 | template 8 | using Mat = array, M>; 9 | template 10 | using Vec = array; 11 | 12 | template 13 | auto identity() { 14 | Mat a = {}; 15 | for (unsigned i = 0; i < M; i++) 16 | a[i][i] = 1; 17 | return a; 18 | } 19 | 20 | template 21 | auto operator+(const Mat& a, const Mat& b) { 22 | Mat c = {}; 23 | for (unsigned i = 0; i < M; i++) 24 | for (unsigned j = 0; j < M; j++) 25 | c[i][j] = a[i][j] + b[i][j]; 26 | return c; 27 | } 28 | 29 | template 30 | auto operator*(const Mat& a, const Mat& b) { 31 | Mat c = {}; 32 | for (unsigned i = 0; i < M; i++) 33 | for (unsigned k = 0; k < M; k++) 34 | for (unsigned j = 0; j < M; j++) 35 | c[i][j] += a[i][k] * b[k][j]; 36 | return c; 37 | } 38 | 39 | template 40 | auto operator*(const Mat& a, const Vec& b) { 41 | Vec c = {}; 42 | for (unsigned i = 0; i < M; i++) 43 | for (unsigned j = 0; j < M; j++) 44 | c[i] += a[i][j] * b[j]; 45 | return c; 46 | } 47 | 48 | template 49 | auto operator^(const Mat& a, int64_t k) { 50 | Vec b = a, c = identity(); 51 | while (k > 0) { 52 | if (k & 1) 53 | c = c * b; 54 | if (k >>= 1) 55 | b = b * b; 56 | } 57 | return c; 58 | } 59 | 60 | } // namespace std 61 | 62 | // Matrix cache for fast matrix exponentiation 63 | template 64 | struct matrix_cache { 65 | Mat A, jmp[B][S + 1]; 66 | 67 | explicit matrix_cache(Mat A) : A(A) { build(); } 68 | 69 | void build() { 70 | jmp[0][0] = identity(); 71 | for (int s = 1; s <= S; s++) { 72 | jmp[0][s] = A * jmp[0][s - 1]; 73 | } 74 | for (int b = 1; b < B; b++) { 75 | jmp[b][0] = identity(); 76 | for (int s = 1; s <= S; s++) { 77 | jmp[b][s] = jmp[b - 1][S] * jmp[b][s - 1]; 78 | } 79 | } 80 | } 81 | 82 | auto get(int64_t k) const { 83 | assert(k >= 0); 84 | auto m = jmp[0][k % S]; 85 | k /= S; 86 | for (int b = 1; b < B && k > 0; b++, k /= S) { 87 | m = m * jmp[b][k % S]; 88 | } 89 | return m; 90 | } 91 | }; 92 | 93 | template 94 | struct Fibonacci { 95 | matrix_cache cache; 96 | 97 | Fibonacci() : cache({{{0, 1}, {1, 1}}}) {} 98 | 99 | // Fib(x) 100 | auto get(int64_t x) const { return cache.get(x + 1)[0][0]; } 101 | 102 | // {Fib(x),Fib(x+1)} 103 | auto get_pair(int64_t x) const { return cache.get(x + 1)[0]; } 104 | 105 | // Given a=SUM Fib(x) and b=SUM Fib(x+1), compute SUM Fib(x+c) and SUM Fib(x+1+c) 106 | auto shift(V a, V b, int64_t c) { return cache.get(c) * Vec({a, b}); } 107 | }; 108 | --------------------------------------------------------------------------------