diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..91bcaa7 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,21 @@ +on: + push: + branches: + - main + pull_request: + +jobs: + test: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Configure tests + run: cmake . -B build + + - name: Build tests + run: cmake --build build --target all --parallel + + - name: Run tests + run: ctest --test-dir build --output-on-failure -j diff --git a/.gitignore b/.gitignore index 173cbda..ceb1614 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,10 @@ !library/ !leetcode/ +!test/ +!.github/ !.clang-format !ruff.toml !README.md +!CMakeLists.txt !ac-library diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..fc70e04 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,8 @@ +cmake_minimum_required(VERSION 3.30) +project(competitive-programming CXX) + +include(CTest) + +if(BUILD_TESTING) + add_subdirectory("test") +endif() diff --git a/library/.gitignore b/library/.gitignore new file mode 100644 index 0000000..c034d3f --- /dev/null +++ b/library/.gitignore @@ -0,0 +1 @@ +**/.cache \ No newline at end of file diff --git a/library/cpp/data_structures/union_find.hpp b/library/cpp/data_structure/union_find.hpp similarity index 100% rename from library/cpp/data_structures/union_find.hpp rename to library/cpp/data_structure/union_find.hpp diff --git a/test/.gitignore b/test/.gitignore new file mode 100644 index 0000000..7f1b4d7 --- /dev/null +++ b/test/.gitignore @@ -0,0 +1,4 @@ +**/__pycache__ +build/ +.cache/ +compile_commands.json \ No newline at end of file diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..e741036 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,41 @@ +find_package(Python COMPONENTS Interpreter REQUIRED) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +include_directories("../library/cpp") + +function(add_library_test name) + add_executable(${name}_program "program/${name}.cpp") + add_executable(${name}_solution "solution/${name}.cpp") + + add_test(NAME ${name} + COMMAND ${Python_EXECUTABLE} -m test.checker.${name} -p $ -s $ + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + ) +endfunction() + +# data_structure +add_library_test("union_find") + +# graph +add_library_test("bridges") +add_library_test("lowest_common_ancestor") +add_library_test("strongly_connected_components") + +# math +add_library_test("combinatorics") +add_library_test("matrix") + +# range_query +add_library_test("fenwick") +add_library_test("heavy_light_decomposition") +add_library_test("mo_array") +add_library_test("segment_tree") +add_library_test("segment_tree_lazy") +add_library_test("sparse_table") +add_library_test("treap") + +# string +add_library_test("aho_corasick") +add_library_test("rolling_hash") diff --git a/test/__init__.py b/test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/checker/aho_corasick.py b/test/checker/aho_corasick.py new file mode 100644 index 0000000..a719eb8 --- /dev/null +++ b/test/checker/aho_corasick.py @@ -0,0 +1,35 @@ +from test.lib import cli, runner +from random import randint, choices +from string import ascii_lowercase + + +def main(): + args = cli.args.parse_args() + iter = args.iterations + for _ in range(iter): + stdin = [] + + n = randint(1, 10**5) + s = "".join(choices(ascii_lowercase, k=n)) + stdin.append(s) + + tot = 5 * 10**5 + k_max = randint(1, 5 * 10**5) + patterns = [] + for _ in range(k_max): + if tot == 0: + break + m = randint(1, tot) + p = "".join(choices(ascii_lowercase, k=m)) + patterns.append(p) + tot -= m + + stdin.append(str(len(patterns))) + stdin.extend(patterns) + + proc_ans, sol_ans = runner.run(args.program, args.solution, "\n".join(stdin)) + assert proc_ans == sol_ans + + +if __name__ == "__main__": + main() diff --git a/test/checker/bridges.py b/test/checker/bridges.py new file mode 100644 index 0000000..60be0a9 --- /dev/null +++ b/test/checker/bridges.py @@ -0,0 +1,32 @@ +from test.lib import cli, runner, random +from random import randint +from itertools import batched + + +def main(): + args = cli.args.parse_args() + iter = args.iterations + for _ in range(iter): + stdin = [] + + n = randint(1, 10**5) + m = randint(n - 1, min(10**5, n * (n - 1) // 2)) + stdin.append(f"{n} {m}") + + ed = random.rand_connected_graph_edges(n, m) + for a, b in ed: + stdin.append(f"{a} {b}") + + proc_ans, sol_ans = runner.run(args.program, args.solution, "\n".join(stdin)) + + proc_ans = proc_ans.split() + sol_ans = sol_ans.split() + assert proc_ans[0] == sol_ans[0] + + proc_ed = sorted([(min(a, b), max(a, b)) for a, b in batched(proc_ans[1:], 2)]) + sol_ed = sorted([(min(a, b), max(a, b)) for a, b in batched(sol_ans[1:], 2)]) + assert proc_ed == sol_ed + + +if __name__ == "__main__": + main() diff --git a/test/checker/combinatorics.py b/test/checker/combinatorics.py new file mode 100644 index 0000000..dee6008 --- /dev/null +++ b/test/checker/combinatorics.py @@ -0,0 +1,24 @@ +from test.lib import cli, runner +from random import randint + + +def main(): + args = cli.args.parse_args() + iter = args.iterations + for _ in range(iter): + stdin = [] + + n = randint(1, 10**5) + stdin.append(str(n)) + + for _ in range(n): + a = randint(0, 10**6) + b = randint(0, a) + stdin.append(f"{a} {b}") + + proc_ans, sol_ans = runner.run(args.program, args.solution, "\n".join(stdin)) + assert proc_ans == sol_ans + + +if __name__ == "__main__": + main() diff --git a/test/checker/fenwick.py b/test/checker/fenwick.py new file mode 100644 index 0000000..06f790d --- /dev/null +++ b/test/checker/fenwick.py @@ -0,0 +1,34 @@ +from test.lib import cli, runner +from random import randint + + +def main(): + args = cli.args.parse_args() + iter = args.iterations + for _ in range(iter): + stdin = [] + + n = randint(1, 2 * 10**5) + q = randint(1, 2 * 10**5) + stdin.append(f"{n} {q}") + + x = [randint(1, 10**9) for _ in range(n)] + stdin.append(" ".join(map(str, x))) + + for _ in range(q): + type = randint(1, 2) + if type == 1: + k = randint(1, n) + u = randint(1, 10**9) + stdin.append(f"1 {k} {u}") + else: + a = randint(1, n) + b = randint(a, n) + stdin.append(f"2 {a} {b}") + + proc_ans, sol_ans = runner.run(args.program, args.solution, "\n".join(stdin)) + assert proc_ans == sol_ans + + +if __name__ == "__main__": + main() diff --git a/test/checker/heavy_light_decomposition.py b/test/checker/heavy_light_decomposition.py new file mode 100644 index 0000000..e27831e --- /dev/null +++ b/test/checker/heavy_light_decomposition.py @@ -0,0 +1,38 @@ +from test.lib import cli, runner, random +from random import randint + + +def main(): + args = cli.args.parse_args() + iter = args.iterations + for _ in range(iter): + stdin = [] + + n = randint(1, 2 * 10**5) + q = randint(1, 2 * 10**5) + stdin.append(f"{n} {q}") + + v = [str(randint(1, 10**9)) for _ in range(n)] + stdin.append(" ".join(v)) + + ed = random.rand_tree_edges(n) + for a, b in ed: + stdin.append(f"{a} {b}") + + for _ in range(q): + type = randint(1, 2) + if type == 1: + s = randint(1, n) + x = randint(1, 10**9) + stdin.append(f"1 {s} {x}") + else: + a = randint(1, n) + b = randint(1, n) + stdin.append(f"2 {a} {b}") + + proc_ans, sol_ans = runner.run(args.program, args.solution, "\n".join(stdin)) + assert proc_ans == sol_ans + + +if __name__ == "__main__": + main() diff --git a/test/checker/lowest_common_ancestor.py b/test/checker/lowest_common_ancestor.py new file mode 100644 index 0000000..ea6ea59 --- /dev/null +++ b/test/checker/lowest_common_ancestor.py @@ -0,0 +1,35 @@ +from test.lib import cli, runner, random +from random import randint +import sys +import resource + +sys.setrecursionlimit(10**9) +resource.setrlimit( + resource.RLIMIT_STACK, (resource.RLIM_INFINITY, resource.RLIM_INFINITY) +) + + +def main(): + args = cli.args.parse_args() + iter = args.iterations + for _ in range(iter): + stdin = [] + + n = randint(1, 2 * 10**5) + q = randint(1, 2 * 10**5) + stdin.append(f"{n} {q}") + + par = random.rand_tree_parent_list(n=n)[1:] + stdin.append(" ".join(map(str, par))) + + for _ in range(q): + a = randint(1, n) + b = randint(1, n) + stdin.append(f"{a} {b}") + + proc_ans, sol_ans = runner.run(args.program, args.solution, "\n".join(stdin)) + assert proc_ans == sol_ans + + +if __name__ == "__main__": + main() diff --git a/test/checker/matrix.py b/test/checker/matrix.py new file mode 100644 index 0000000..f394ca6 --- /dev/null +++ b/test/checker/matrix.py @@ -0,0 +1,21 @@ +from test.lib import cli, runner +from random import randint + + +def main(): + args = cli.args.parse_args() + iter = args.iterations + for _ in range(iter): + stdin = [] + + n = randint(3, 10**9) + l = randint(1, 75) + r = randint(l, 75) + stdin.append(f"{n} {l} {r}") + + proc_ans, sol_ans = runner.run(args.program, args.solution, "\n".join(stdin)) + assert len(proc_ans) == len(sol_ans) + + +if __name__ == "__main__": + main() diff --git a/test/checker/mo_array.py b/test/checker/mo_array.py new file mode 100644 index 0000000..cf932d5 --- /dev/null +++ b/test/checker/mo_array.py @@ -0,0 +1,30 @@ +from test.lib import cli, runner +from random import randint + + +def main(): + args = cli.args.parse_args() + iter = args.iterations + for _ in range(iter): + stdin = [] + + n = randint(1, 2 * 10**5) + t = randint(1, 2 * 10**5) + stdin.append(f"{n} {t}") + + a = [randint(1, 10**6) for _ in range(n)] + stdin.append(" ".join(map(str, a))) + + for _ in range(t): + a = randint(1, n) + b = randint(a, n) + stdin.append(f"{a} {b}") + + proc_ans, sol_ans = runner.run(args.program, args.solution, "\n".join(stdin)) + if proc_ans != sol_ans: + print(proc_ans) + assert proc_ans == sol_ans + + +if __name__ == "__main__": + main() diff --git a/test/checker/rolling_hash.py b/test/checker/rolling_hash.py new file mode 100644 index 0000000..76390cb --- /dev/null +++ b/test/checker/rolling_hash.py @@ -0,0 +1,22 @@ +from test.lib import cli, runner +from random import randint, choices +from string import ascii_lowercase + + +def main(): + args = cli.args.parse_args() + iter = args.iterations + for _ in range(iter): + stdin = [] + + n = randint(1, 10**5) + s = "".join(choices(ascii_lowercase, k=n)) + stdin.append(s) + + proc_ans, sol_ans = runner.run(args.program, args.solution, "\n".join(stdin)) + assert len(proc_ans) == len(sol_ans) + assert proc_ans in s and sol_ans in s + + +if __name__ == "__main__": + main() diff --git a/test/checker/segment_tree.py b/test/checker/segment_tree.py new file mode 100644 index 0000000..286fe2b --- /dev/null +++ b/test/checker/segment_tree.py @@ -0,0 +1,29 @@ +from test.lib import cli, runner +from random import randint + + +def main(): + args = cli.args.parse_args() + iter = args.iterations + for _ in range(iter): + stdin = [] + + n = randint(1, 2 * 10**5) + q = randint(1, 2 * 10**5) + stdin.append(f"{n} {q}") + + x = [randint(-(10**9), 10**9) for _ in range(n)] + stdin.append(" ".join(map(str, x))) + + for _ in range(q): + type = randint(1, 2) + a = randint(1, n) + b = randint(a, n) + stdin.append(f"{type} {a} {b}") + + proc_ans, sol_ans = runner.run(args.program, args.solution, "\n".join(stdin)) + assert proc_ans == sol_ans + + +if __name__ == "__main__": + main() diff --git a/test/checker/segment_tree_lazy.py b/test/checker/segment_tree_lazy.py new file mode 100644 index 0000000..a3514ec --- /dev/null +++ b/test/checker/segment_tree_lazy.py @@ -0,0 +1,29 @@ +from test.lib import cli, runner +from random import randint + + +def main(): + args = cli.args.parse_args() + iter = args.iterations + for _ in range(iter): + stdin = [] + + n = randint(1, 2 * 10**5) + q = randint(1, 2 * 10**5) + stdin.append(f"{n} {q}") + + t = [randint(1, 10**6) for _ in range(n)] + stdin.append(" ".join(map(str, t))) + + for _ in range(q): + type = randint(1, 2) + a = randint(1, n) + b = randint(a, n) + stdin.append(f"{type} {a} {b}") + + proc_ans, sol_ans = runner.run(args.program, args.solution, "\n".join(stdin)) + assert proc_ans == sol_ans + + +if __name__ == "__main__": + main() diff --git a/test/checker/sparse_table.py b/test/checker/sparse_table.py new file mode 100644 index 0000000..f63b29a --- /dev/null +++ b/test/checker/sparse_table.py @@ -0,0 +1,28 @@ +from test.lib import cli, runner +from random import randint + + +def main(): + args = cli.args.parse_args() + iter = args.iterations + for _ in range(iter): + stdin = [] + + n = randint(1, 2 * 10**5) + q = randint(1, 2 * 10**5) + stdin.append(f"{n} {q}") + + x = [randint(1, 10**9) for _ in range(n)] + stdin.append(" ".join(map(str, x))) + + for _ in range(q): + a = randint(1, n) + b = randint(a, n) + stdin.append(f"{a} {b}") + + proc_ans, sol_ans = runner.run(args.program, args.solution, "\n".join(stdin)) + assert proc_ans == sol_ans + + +if __name__ == "__main__": + main() diff --git a/test/checker/strongly_connected_components.py b/test/checker/strongly_connected_components.py new file mode 100644 index 0000000..a14b67c --- /dev/null +++ b/test/checker/strongly_connected_components.py @@ -0,0 +1,39 @@ +from test.lib import cli, runner +from random import randint +from collections import defaultdict + + +def main(): + args = cli.args.parse_args() + iter = args.iterations + for _ in range(iter): + stdin = [] + + n = randint(1, 10**5) + m = randint(1, 2 * 10**5) + stdin.append(f"{n} {m}") + for _ in range(m): + a = randint(1, n) + b = randint(1, n) + stdin.append(f"{a} {b}") + + proc_ans, sol_ans = runner.run(args.program, args.solution, "\n".join(stdin)) + proc_ans = proc_ans.split() + sol_ans = sol_ans.split() + assert proc_ans[0] == sol_ans[0] + + proc_kingdoms = defaultdict(list) + for i in range(1, n + 1): + proc_kingdoms[proc_ans[i]].append(i) + proc_groups = sorted(proc_kingdoms.values()) + + sol_kingdoms = defaultdict(list) + for i in range(1, n + 1): + sol_kingdoms[sol_ans[i]].append(i) + sol_groups = sorted(sol_kingdoms.values()) + + assert proc_groups == sol_groups + + +if __name__ == "__main__": + main() diff --git a/test/checker/treap.py b/test/checker/treap.py new file mode 100644 index 0000000..24bf2bb --- /dev/null +++ b/test/checker/treap.py @@ -0,0 +1,25 @@ +from test.lib import cli, runner +from random import randint + + +def main(): + args = cli.args.parse_args() + iter = args.iterations + for _ in range(iter): + stdin = [] + + n = randint(1, 2 * 10**5) + q = randint(1, 2 * 10**5) + stdin.append(f"{n} {q}") + + for _ in range(q): + a = randint(1, n) + b = randint(a, n) + stdin.append(f"{a} {b}") + + proc_ans, sol_ans = runner.run(args.program, args.solution, "\n".join(stdin)) + assert proc_ans == sol_ans + + +if __name__ == "__main__": + main() diff --git a/test/checker/union_find.py b/test/checker/union_find.py new file mode 100644 index 0000000..8ade983 --- /dev/null +++ b/test/checker/union_find.py @@ -0,0 +1,24 @@ +from test.lib import cli, runner +from random import randint + + +def main(): + args = cli.args.parse_args() + iter = args.iterations + for _ in range(iter): + stdin = [] + + n = randint(1, 10**5) + m = randint(1, 2 * 10**5) + stdin.append(f"{n} {m}") + for _ in range(m): + a = randint(1, n) + b = randint(1, n) + stdin.append(f"{a} {b}") + + proc_ans, sol_ans = runner.run(args.program, args.solution, "\n".join(stdin)) + assert proc_ans == sol_ans + + +if __name__ == "__main__": + main() diff --git a/test/lib/__init__.py b/test/lib/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/lib/cli.py b/test/lib/cli.py new file mode 100644 index 0000000..a05dbf4 --- /dev/null +++ b/test/lib/cli.py @@ -0,0 +1,12 @@ +import argparse + +args = argparse.ArgumentParser(description="Process some arguments.") +_ = args.add_argument( + "--program", "-p", type=str, required=True, help="Path to the program" +) +_ = args.add_argument( + "--solution", "-s", type=str, required=True, help="Path to the solution" +) +_ = args.add_argument( + "--iterations", "-i", type=int, default=10, help="Number of iterations" +) diff --git a/test/lib/random.py b/test/lib/random.py new file mode 100644 index 0000000..ee7bbd7 --- /dev/null +++ b/test/lib/random.py @@ -0,0 +1,79 @@ +from random import randint, sample + + +def rand_tree_edges(n: int) -> list[tuple[int, int]]: + """ + Generates a random tree in linear time using random Prufer Code. + + The tree is truly random, meaning it has on average `sqrt(N)` height, thus it is very + unlikely to get "edge case" trees such as a single chain. + """ + code = [randint(0, n - 1) for _ in range(n - 2)] + deg = [1] * n + for i in code: + deg[i] += 1 + ptr = 0 + while deg[ptr] != 1: + ptr += 1 + leaf = ptr + ed = [] + for v in code: + ed.append((leaf + 1, v + 1)) + deg[v] -= 1 + if deg[v] == 1 and v < ptr: + leaf = v + else: + ptr += 1 + while deg[ptr] != 1: + ptr += 1 + leaf = ptr + ed.append((ptr + 1, n)) + return ed + + +def rand_tree_parent_list(n: int, root: int = 1) -> list[int]: + ed = rand_tree_edges(n) + g = [[] for _ in range(n + 1)] + for a, b in ed: + g[a].append(b) + g[b].append(a) + ans = [-1] * (n + 1) + + def dfs(u, p): + ans[u] = p + for v in g[u]: + if v != p: + dfs(v, u) + + dfs(root, -1) + return ans[1:] + + +def rand_connected_graph_edges(n: int, m: int): + assert n - 1 <= m <= n * (n - 1) // 2 + ed = rand_tree_edges(n) + ex = set() + deg = [0] * (n + 1) + for a, b in ed: + ex.add((a, b)) + ex.add((b, a)) + deg[a] += 1 + deg[b] += 1 + + choices = [] + for i in range(1, n + 1): + if deg[i] < n - 1: + choices.append(i) + + for _ in range(m - len(ed)): + assert len(choices) >= 2 + a, b = sample(choices, 2) + ed.append((a, b)) + deg[a] += 1 + deg[b] += 1 + if deg[a] == n - 1: + deg.remove(a) + if deg[b] == n - 1: + deg.remove(b) + + return ed diff --git a/test/lib/runner.py b/test/lib/runner.py new file mode 100644 index 0000000..119f1d8 --- /dev/null +++ b/test/lib/runner.py @@ -0,0 +1,29 @@ +import subprocess + + +def run(program: str, solution: str, stdin: str) -> tuple[str, str]: + """ + Executes a given program and solution with provided stdin. + """ + + input = stdin.encode() + + try: + proc_output = subprocess.check_output(program, input=input) + except subprocess.CalledProcessError as error: + print(f"Program crashed with error: {error.stderr}") + raise error + + try: + sol_output = subprocess.check_output(solution, input=input) + except subprocess.CalledProcessError as error: + print(f"Solution crashed with error: {error.stderr}") + raise error + + proc_ans = proc_output.decode().strip() + proc_ans = " ".join(proc_ans.split()) + + sol_ans = sol_output.decode().strip() + sol_ans = " ".join(sol_ans.split()) + + return (proc_ans, sol_ans) diff --git a/test/program/aho_corasick.cpp b/test/program/aho_corasick.cpp new file mode 100644 index 0000000..3546d26 --- /dev/null +++ b/test/program/aho_corasick.cpp @@ -0,0 +1,44 @@ +#include +using namespace std; + +#include + +int main() { + string s; + int k; + cin >> s >> k; + vector p(k); + for (auto &v : p) { + cin >> v; + } + auto g = aho_corasick_automaton<'a', 'z'>(p); + int m = g.size(); + vector> rlink(m); + for (int i = 1; i < m; i++) { + rlink[g[i].link].push_back(i); + } + vector dp(m, 1e9); + for (int i = 0, u = 0; i < s.size(); i++) { + int c = s[i] - 'a'; + u = g[u].next[c]; + int p = u; + if (g[p].matches.empty()) { + p = g[p].exit; + } + dp[p] = min(dp[p], i + 1); + } + vector ans(k, -1); + auto dfs = [&](auto &&self, int u) -> int { + for (int l : rlink[u]) { + dp[u] = min(dp[u], self(self, l)); + } + for (int p : g[u].matches) { + ans[p] = dp[u]; + } + return dp[u]; + }; + dfs(dfs, 0); + for (int i = 0; i < k; i++) { + cout << (ans[i] == 1e9 ? -1 : ans[i] - (int)p[i].size() + 1) << '\n'; + } +} \ No newline at end of file diff --git a/test/program/bridges.cpp b/test/program/bridges.cpp new file mode 100644 index 0000000..5b5e157 --- /dev/null +++ b/test/program/bridges.cpp @@ -0,0 +1,39 @@ +#include +using namespace std; + +#include + +class Solution { +public: + vector> criticalConnections(int n, + vector> &connections) { + vector> g(n); + for (auto &e : connections) { + g[e[0]].push_back(e[1]); + g[e[1]].push_back(e[0]); + } + vector> ans; + for (auto &[a, b] : bridges(g)) { + ans.push_back({a, b}); + } + return ans; + } +}; + +int main() { + cin.tie(0)->sync_with_stdio(false); + int n, m; + cin >> n >> m; + vector> ed; + for (int i = 0; i < m; i++) { + int a, b; + cin >> a >> b; + a--, b--; + ed.push_back({a, b}); + } + auto ans = Solution().criticalConnections(n, ed); + cout << ans.size() << '\n'; + for (auto &v : ans) { + cout << v[0] << ' ' << v[1] << '\n'; + } +} \ No newline at end of file diff --git a/test/program/combinatorics.cpp b/test/program/combinatorics.cpp new file mode 100644 index 0000000..8a1fe56 --- /dev/null +++ b/test/program/combinatorics.cpp @@ -0,0 +1,16 @@ +#include +using namespace std; + +#include + +int main() { + cin.tie(0)->sync_with_stdio(false); + int n; + cin >> n; + combinatorics<(int)1e9 + 7> comb(1e6); + for (int i = 0; i < n; i++) { + int a, b; + cin >> a >> b; + cout << comb.ncr(a, b).value() << '\n'; + } +} \ No newline at end of file diff --git a/test/program/fenwick.cpp b/test/program/fenwick.cpp new file mode 100644 index 0000000..e0d9e83 --- /dev/null +++ b/test/program/fenwick.cpp @@ -0,0 +1,29 @@ +#include +using namespace std; + +#include + +int main() { + cin.tie(0)->sync_with_stdio(false); + int n, q; + cin >> n >> q; + vector x(n); + for (auto &v : x) { + cin >> v; + } + fenwick fw(x, plus{}); + for (int i = 0; i < q; i++) { + int t; + cin >> t; + if (t == 1) { + int k, u; + cin >> k >> u; + fw.update(k, -x[k - 1] + u); + x[k - 1] = u; + } else { + int a, b; + cin >> a >> b; + cout << fw.query(b) - fw.query(a - 1) << '\n'; + } + } +} \ No newline at end of file diff --git a/test/program/heavy_light_decomposition.cpp b/test/program/heavy_light_decomposition.cpp new file mode 100644 index 0000000..dd38d31 --- /dev/null +++ b/test/program/heavy_light_decomposition.cpp @@ -0,0 +1,40 @@ +#include +using namespace std; + +#include + +int main() { + cin.tie(0)->sync_with_stdio(false); + int n, q; + cin >> n >> q; + vector t(n); + for (auto &v : t) { + cin >> v; + } + vector> g(n); + for (int i = 0; i < n - 1; i++) { + int a, b; + cin >> a >> b; + a--, b--; + g[a].push_back(b); + g[b].push_back(a); + } + heavy_light_decomposition hld(g, t, 0, + [](int a, int b) { return max(a, b); }); + for (int i = 0; i < q; i++) { + int t; + cin >> t; + if (t == 1) { + int s, x; + cin >> s >> x; + s--; + hld.set(s, x); + } else { + int a, b; + cin >> a >> b; + a--, b--; + cout << hld.query(a, b) << ' '; + } + } + cout << '\n'; +} \ No newline at end of file diff --git a/test/program/lowest_common_ancestor.cpp b/test/program/lowest_common_ancestor.cpp new file mode 100644 index 0000000..3d503ae --- /dev/null +++ b/test/program/lowest_common_ancestor.cpp @@ -0,0 +1,23 @@ +#include +using namespace std; + +#include + +int main() { + cin.tie(0)->sync_with_stdio(false); + int n, q; + cin >> n >> q; + vector p(n + 1); + vector g(n + 1, vector()); + for (int i = 0; i < n - 1; i++) { + int b; + cin >> b; + g[b].push_back(i + 2); + } + lowest_common_ancestor lca(g, 1); + for (int i = 0; i < q; i++) { + int a, b; + cin >> a >> b; + cout << lca.query(a, b) << '\n'; + } +} \ No newline at end of file diff --git a/test/program/matrix.cpp b/test/program/matrix.cpp new file mode 100644 index 0000000..01dc0a8 --- /dev/null +++ b/test/program/matrix.cpp @@ -0,0 +1,34 @@ +#include +using namespace std; + +#include +#include + +class Solution { +public: + int zigZagArrays(int n, int l, int r) { + int m = r - l + 1; + if (n == 1) + return m; + const int M = 1e9 + 7; + matrix> t(2 * m, 2 * m, 0); + for (int i = 0; i < m; i++) { + for (int j = i + 1; j < m; j++) + t.at(i, j + m) = 1; + for (int j = 0; j < i; j++) + t.at(i + m, j) = 1; + } + matrix> a(2 * m, 1, 1); + auto dp = t.pow(n - 1) * a; + modint ans; + for (int i = 0; i < 2 * m; i++) + ans += dp.at(i, 0); + return ans.value(); + } +}; + +int main() { + int n, l, r; + cin >> n >> l >> r; + cout << Solution().zigZagArrays(n, l, r) << '\n'; +} \ No newline at end of file diff --git a/test/program/mo_array.cpp b/test/program/mo_array.cpp new file mode 100644 index 0000000..c9c1ca8 --- /dev/null +++ b/test/program/mo_array.cpp @@ -0,0 +1,38 @@ +#include +using namespace std; + +#include +#include + +int main() { + cin.tie(0)->sync_with_stdio(false); + int n, q; + cin >> n >> q; + vector a(n); + for (auto &v : a) { + cin >> v; + } + vector cnt(1e6 + 1); + long long cur_ans = 0; + auto add = [&](int i) -> void { + cur_ans -= 1LL * cnt[a[i]] * cnt[a[i]] * a[i]; + cnt[a[i]]++; + cur_ans += 1LL * cnt[a[i]] * cnt[a[i]] * a[i]; + }; + + auto remove = [&](int i) -> void { + cur_ans -= 1LL * cnt[a[i]] * cnt[a[i]] * a[i]; + cnt[a[i]]--; + cur_ans += 1LL * cnt[a[i]] * cnt[a[i]] * a[i]; + }; + auto get_answer = [&]() -> long long { return cur_ans; }; + mo_array mo(add, remove, get_answer); + for (int i = 0; i < q; i++) { + int l, r; + cin >> l >> r; + mo.query(l - 1, r - 1); + } + for (auto v : mo.solve(&hilbert)) { + cout << v << ' '; + } +} \ No newline at end of file diff --git a/test/program/rolling_hash.cpp b/test/program/rolling_hash.cpp new file mode 100644 index 0000000..f175f04 --- /dev/null +++ b/test/program/rolling_hash.cpp @@ -0,0 +1,37 @@ +#include +using namespace std; + +#include + +int main() { + cin.tie(0)->sync_with_stdio(false); + string S; + cin >> S; + int N = S.size(); + rolling_hash hs(S); + int l = 0, r = N; + int p = -1; + while (r - l > 1) { + int m = (l + r) >> 1; + unordered_set st; + bool ok = false; + for (int i = 0; i + m <= N; i++) { + auto h = hs.hash(i, i + m); + if (st.find(h) != st.end()) { + p = i; + ok = true; + } + st.insert(h); + } + if (ok) { + l = m; + } else { + r = m; + } + } + if (p == -1) { + cout << "-1\n"; + } else { + cout << S.substr(p, l) << '\n'; + } +} \ No newline at end of file diff --git a/test/program/segment_tree.cpp b/test/program/segment_tree.cpp new file mode 100644 index 0000000..8c9eb74 --- /dev/null +++ b/test/program/segment_tree.cpp @@ -0,0 +1,36 @@ +#include +using namespace std; + +#include + +int main() { + int n, m; + cin >> n >> m; + vector x(n); + for (auto &v : x) { + cin >> v; + } + struct S { + long long sum = 0, mxl = 0, mxr = 0, mx = 0; + }; + auto cb = [](const S &a, const S &b) -> S { + S s; + s.sum = a.sum + b.sum; + s.mxl = max(a.mxl, a.sum + b.mxl); + s.mxr = max(b.mxr, a.mxr + b.sum); + s.mx = max(s.sum, max(a.mx, b.mx)); + s.mx = max(s.mx, max(s.mxl, s.mxr)); + s.mx = max(s.mx, a.mxr + b.mxl); + return s; + }; + segment_tree seg(n, S{}, cb); + for (int i = 0; i < n; i++) { + int v = max(x[i], 0); + seg.set(i, S{x[i], v, v, v}); + } + for (int i = 0; i < m; i++) { + int k, v; + cin >> k >> v; + cout << seg.query(k - 1, v).mx << '\n'; + } +} \ No newline at end of file diff --git a/test/program/segment_tree_lazy.cpp b/test/program/segment_tree_lazy.cpp new file mode 100644 index 0000000..9653440 --- /dev/null +++ b/test/program/segment_tree_lazy.cpp @@ -0,0 +1,43 @@ +#include +using namespace std; + +#include + +int main() { + int n, q; + cin >> n >> q; + vector t(n); + for (auto &v : t) { + cin >> v; + } + struct S { + int l, r; + long long s; + }; + struct D { + int c; + long long s; + }; + auto g = [](long long x) -> long long { return x * (x + 1) / 2; }; + auto apply = [&](S s, D d, int k) -> S { + return {s.l, s.r, s.s + (g(s.r) - g(s.l - 1)) * d.c - d.s * k}; + }; + auto push = [](D a, D b, int) -> D { return {a.c + b.c, a.s + b.s}; }; + auto cb = [&](S a, S b) -> S { return {a.l, b.r, a.s + b.s}; }; + vector init(n); + for (int i = 0; i < n; i++) { + init[i].l = init[i].r = i; + init[i].s = t[i]; + } + segment_tree_lazy seg(init, S{}, D{}, apply, push, cb); + for (int i = 0; i < q; i++) { + int t, a, b; + cin >> t >> a >> b; + a--, b--; + if (t == 1) { + seg.update(a, b + 1, D{1, a - 1}); + } else { + cout << seg.query(a, b + 1).s << '\n'; + } + } +} \ No newline at end of file diff --git a/test/program/sparse_table.cpp b/test/program/sparse_table.cpp new file mode 100644 index 0000000..9d8963f --- /dev/null +++ b/test/program/sparse_table.cpp @@ -0,0 +1,20 @@ +#include +using namespace std; + +#include + +int main() { + cin.tie(0)->sync_with_stdio(false); + int n, q; + cin >> n >> q; + vector x(n); + for (auto &v : x) { + cin >> v; + } + sparse_table sp(x, [](int a, int b) { return min(a, b); }); + for (int i = 0; i < q; i++) { + int a, b; + cin >> a >> b; + cout << sp.query(a - 1, b - 1) << '\n'; + } +} \ No newline at end of file diff --git a/test/program/strongly_connected_components.cpp b/test/program/strongly_connected_components.cpp new file mode 100644 index 0000000..217eced --- /dev/null +++ b/test/program/strongly_connected_components.cpp @@ -0,0 +1,28 @@ +#include +using namespace std; + +#include + +int main() { + int n, m; + cin >> n >> m; + vector> g(n + 1); + for (int i = 0; i < m; i++) { + int a, b; + cin >> a >> b; + g[a].push_back(b); + } + int k = 0; + vector ans(n + 1); + for (auto &scc : strongly_connected_components(g)) { + k++; + for (int v : scc) { + ans[v] = k; + } + } + cout << k - 1 << '\n'; + for (int i = 1; i <= n; i++) { + cout << ans[i] << ' '; + } + cout << '\n'; +} \ No newline at end of file diff --git a/test/program/treap.cpp b/test/program/treap.cpp new file mode 100644 index 0000000..c2b6d44 --- /dev/null +++ b/test/program/treap.cpp @@ -0,0 +1,28 @@ +#include +using namespace std; + +#include + +int main() { + int n, m; + string s; + cin >> n >> m >> s; + vector v(s.begin(), s.end()); + treap t(v, plus{}); + for (int i = 0; i < m; i++) { + int l, r; + cin >> l >> r; + l--, r--; + using node_t = decltype(t)::node; + node_t *tl = nullptr, *tm = nullptr, *tr = nullptr; + t.split(t.root, l, tl, tm); + t.split(tm, r - l + 1, tm, tr); + t.merge(tl, tr, t.root); + t.merge(t.root, tm, t.root); + } + auto ans = t.data(t.root); + for (char c : ans) { + cout << c; + } + cout << '\n'; +} \ No newline at end of file diff --git a/test/program/union_find.cpp b/test/program/union_find.cpp new file mode 100644 index 0000000..0821c8b --- /dev/null +++ b/test/program/union_find.cpp @@ -0,0 +1,22 @@ +#include +using namespace std; + +#include + +int main() { + cin.tie(0)->sync_with_stdio(false); + int n, m; + cin >> n >> m; + int mx_size = 1, comps = n; + vector size(n + 1, 1); + union_find uf(n + 1, [&](int a, int b) { + size[a] += size[b]; + mx_size = max(mx_size, size[a]); + }); + for (int i = 0; i < m; i++) { + int a, b; + cin >> a >> b; + uf.unite(a, b); + cout << uf.components() - 1 << ' ' << mx_size << '\n'; + } +} \ No newline at end of file diff --git a/test/solution/aho_corasick.cpp b/test/solution/aho_corasick.cpp new file mode 100644 index 0000000..27728ac --- /dev/null +++ b/test/solution/aho_corasick.cpp @@ -0,0 +1,90 @@ +// https://cses.fi/problemset/model/2104/ +// I know this isn't Aho-Corasick but correct implementation also solves it. + +#include +#include +#include +using namespace std; + +struct SuffixAutomaton { + struct State { + int len; + State *link; + map next; + bool final; + int pos; + }; + + State *first, *last; + + SuffixAutomaton(string s) { + first = last = new State{}; + for (auto c : s) { + auto *add = new State{}; + add->len = last->len + 1; + add->link = first; + State *cur = last; + while (cur && !cur->next[c]) { + cur->next[c] = add; + cur = cur->link; + } + if (cur && cur->next[c] != add) { + State *mid = cur->next[c]; + if (cur->len + 1 == mid->len) { + add->link = mid; + } else { + auto *copy = new State(*mid); + copy->len = cur->len + 1; + add->link = mid->link = copy; + while (cur && cur->next[c] == mid) { + cur->next[c] = copy; + cur = cur->link; + } + } + } + last = add; + } + State *cur = last; + while (cur->len) { + cur->final = true; + cur = cur->link; + } + last->pos = s.size(); + dfs(first); + } + + void dfs(State *cur) { + if (cur->pos) + return; + cur->pos = last->pos; + for (auto [c, p] : cur->next) { + dfs(p); + cur->pos = min(cur->pos, p->pos - 1); + } + } + + int pos(string p) { + State *cur = first; + for (auto c : p) { + if (!cur->next[c]) + return -1; + cur = cur->next[c]; + } + return cur->pos - p.size(); + } +}; + +int main() { + string s; + cin >> s; + SuffixAutomaton sa(s); + + int k; + cin >> k; + for (int ki = 1; ki <= k; ki++) { + string p; + cin >> p; + int pos = sa.pos(p); + cout << (pos != -1 ? pos + 1 : -1) << "\n"; + } +} \ No newline at end of file diff --git a/test/solution/bridges.cpp b/test/solution/bridges.cpp new file mode 100644 index 0000000..ccffaa2 --- /dev/null +++ b/test/solution/bridges.cpp @@ -0,0 +1,63 @@ +// https://leetcode.com/problems/critical-connections-in-a-network/solutions/7238751/easiest-approach-c-beats-90/ + +#include +using namespace std; + +class Solution { +public: + vector> criticalConnections(int n, + vector> &connections) { + vector> adj(n); + int e = connections.size(); + for (int i = 0; i < e; i++) { + adj[connections[i][0]].push_back(connections[i][1]); + adj[connections[i][1]].push_back(connections[i][0]); + } + int timer = 0; + vector disc(n, -1); + vector low(n, -1); + int par = -1; + vector> result; + for (int i = 0; i < n; i++) { + if (disc[i] == -1) { + dfs(i, par, disc, low, result, adj, timer); + } + } + return result; + } + void dfs(int i, int par, vector &disc, vector &low, + vector> &result, vector> &adj, int timer) { + disc[i] = low[i] = timer++; + for (int nbr : adj[i]) { + if (nbr == par) { + continue; + } else if (disc[nbr] == -1) { + dfs(nbr, i, disc, low, result, adj, timer); + low[i] = min(low[i], low[nbr]); + if (low[nbr] > disc[i]) { + result.push_back({i, nbr}); + } + } else { + low[i] = min(low[i], disc[nbr]); + } + } + } +}; + +int main() { + cin.tie(0)->sync_with_stdio(false); + int n, m; + cin >> n >> m; + vector> ed; + for (int i = 0; i < m; i++) { + int a, b; + cin >> a >> b; + a--, b--; + ed.push_back({a, b}); + } + auto ans = Solution().criticalConnections(n, ed); + cout << ans.size() << '\n'; + for (auto &v : ans) { + cout << v[0] << ' ' << v[1] << '\n'; + } +} \ No newline at end of file diff --git a/test/solution/combinatorics.cpp b/test/solution/combinatorics.cpp new file mode 100644 index 0000000..e75ceb7 --- /dev/null +++ b/test/solution/combinatorics.cpp @@ -0,0 +1,38 @@ +// https://cses.fi/problemset/model/1079/ + +#include +using namespace std; +using ll = long long; +const int M = 1000000007; +const int N = 1000000; + +int power(int a, int b) { + if (b == 0) + return 1; + ll u = power(a, b / 2); + u = u * u % M; + if (b % 2 == 1) + u = u * a % M; + return u; +} + +int inv(int x) { return power(x, M - 2); } + +ll fac[N + 1]; + +int ncr(int a, int b) { return fac[a] * inv(fac[b] * fac[a - b] % M) % M; } + +int main() { + fac[0] = 1; + for (int i = 1; i <= N; i++) { + fac[i] = fac[i - 1] * i % M; + } + + int n; + cin >> n; + for (int i = 1; i <= n; i++) { + int a, b; + cin >> a >> b; + cout << ncr(a, b) << "\n"; + } +} \ No newline at end of file diff --git a/test/solution/fenwick.cpp b/test/solution/fenwick.cpp new file mode 100644 index 0000000..14e9117 --- /dev/null +++ b/test/solution/fenwick.cpp @@ -0,0 +1,59 @@ +// https://cses.fi/problemset/model/1648/ +// Fenwick Tree also works here. + +#include +using namespace std; +using ll = long long; + +const int TREE_SIZE = 1 << 18; +ll tree[TREE_SIZE * 2]; + +void change(int i, int x) { + i += TREE_SIZE; + tree[i] = x; + while (i > 1) { + i /= 2; + tree[i] = tree[i * 2] + tree[i * 2 + 1]; + } +} + +// Returns the sum of values in range [l, r] +ll get_sum(int l, int r) { + ll res = 0; + l += TREE_SIZE; + r += TREE_SIZE; + while (l <= r) { + if (l % 2 == 1) + res += tree[l++]; + if (r % 2 == 0) + res += tree[r--]; + l /= 2; + r /= 2; + } + return res; +} + +int main() { + int n, q; + cin >> n >> q; + + for (int i = 1; i <= n; ++i) { + int x; + cin >> x; + change(i, x); + } + + for (int qi = 0; qi < q; ++qi) { + int t; + cin >> t; + if (t == 1) { + int k, u; + cin >> k >> u; + change(k, u); + } else { + int l, r; + cin >> l >> r; + cout << get_sum(l, r) << '\n'; + } + } +} \ No newline at end of file diff --git a/test/solution/heavy_light_decomposition.cpp b/test/solution/heavy_light_decomposition.cpp new file mode 100644 index 0000000..006147a --- /dev/null +++ b/test/solution/heavy_light_decomposition.cpp @@ -0,0 +1,162 @@ +// https://cses.fi/problemset/model/2134/ + +#include +#include +#include +using namespace std; + +// This segment tree supports the following operations: +// 1. SegmentTree(v) constructs the tree from the vector v. +// 2. set(k, x) sets the value in position k to x. +// 3. query(l, r) returns the maximum value in range [l, r]. +struct SegmentTree { + int n; + vector tree; + + SegmentTree(vector &v) { + n = 1; + while (n < v.size()) + n *= 2; + tree.resize(n * 2); + for (int i = 0; i < v.size(); ++i) { + tree[i + n] = v[i]; + } + for (int i = n - 1; i > 0; --i) { + tree[i] = max(tree[i * 2], tree[i * 2 + 1]); + } + } + + void set(int k, int x) { + k += n; + tree[k] = x; + while (k > 1) { + k /= 2; + tree[k] = max(tree[k * 2], tree[k * 2 + 1]); + } + } + + int query(int l, int r) { + int ans = 0; + for (l += n, r += n; l <= r; l /= 2, r /= 2) { + if (l % 2 == 1) + ans = max(ans, tree[l++]); + if (r % 2 == 0) + ans = max(ans, tree[r--]); + } + return ans; + } +}; + +using graph = vector>; + +// Heavy-light decomposition. +// Decomposes the given tree into heavy and light paths. +// Provides the following functions: +// 1. `position(x)`: Returns the position of node x in the DFS order array. +// 2. `subtree(x)`: Returns range in the DFS order array that corresponds to the +// subtree of node x. +// 3. `path_query(a, b, f)`: Partitions the path between nodes a and b into +// O(log n) contiguous ranges of the DFS order array. +// Calls f(l, r) for each such range [l, r]. +// The function f may, for example, query/update a +// segment tree. +struct HLD { + int n; + graph &g; + vector parent; + vector jump; + vector total; + vector order; + + HLD(graph &g) : n(g.size()), g(g), parent(n), jump(n), total(n), order(n) { + dfs_size(0); + int order_index = 0; + dfs_hld(0, order_index); + } + + template void path_query(int a, int b, F f) { + for (;; a = parent[jump[a]]) { + if (order[a] < order[b]) + swap(a, b); + if (order[jump[a]] <= order[b]) { + f(order[b], order[a]); + return; + } + f(order[jump[a]], order[a]); + } + } + + int position(int x) { return order[x]; } + pair subtree(int x) { return {order[x], order[x] + total[x] - 1}; } + +private: + void dfs_size(int node) { + total[node] = 1; + for (auto &child : g[node]) { + g[child].erase(find(g[child].begin(), g[child].end(), node)); + dfs_size(child); + total[node] += total[child]; + if (total[child] > total[g[node][0]]) { + swap(child, g[node][0]); + } + } + } + + void dfs_hld(int node, int &order_index) { + order[node] = order_index++; + for (auto child : g[node]) { + parent[child] = node; + if (child == g[node][0]) { + jump[child] = jump[node]; + } else { + jump[child] = child; + } + dfs_hld(child, order_index); + } + } +}; + +int main() { + int n, q; + cin >> n >> q; + + vector v(n); + for (int i = 0; i < n; ++i) { + cin >> v[i]; + } + + graph g(n); + for (int i = 0; i < n - 1; ++i) { + int a, b; + cin >> a >> b; + a--; + b--; + g[a].push_back(b); + g[b].push_back(a); + } + + HLD hld(g); + + vector initial(n); + for (int i = 0; i < n; ++i) { + initial[hld.position(i)] = v[i]; + } + SegmentTree tree(initial); + + for (int qi = 0; qi < q; ++qi) { + int t; + cin >> t; + if (t == 1) { + int s, x; + cin >> s >> x; + tree.set(hld.position(s - 1), x); + } else { + int a, b; + cin >> a >> b; + int ans = 0; + auto update_ans = [&](int l, int r) { ans = max(ans, tree.query(l, r)); }; + hld.path_query(a - 1, b - 1, update_ans); + cout << ans << '\n'; + } + } +} \ No newline at end of file diff --git a/test/solution/lowest_common_ancestor.cpp b/test/solution/lowest_common_ancestor.cpp new file mode 100644 index 0000000..d555aaf --- /dev/null +++ b/test/solution/lowest_common_ancestor.cpp @@ -0,0 +1,68 @@ +// https://cses.fi/problemset/model/1688/ + +#include +#include +using namespace std; + +const int N = 200000; +int jmp[N][20]; +int depth[N]; +vector g[N]; + +void dfs(int node) { + for (int child : g[node]) { + depth[child] = depth[node] + 1; + dfs(child); + } +} + +int lca(int a, int b) { + if (depth[a] < depth[b]) { + swap(a, b); + } + int depth_difference = depth[a] - depth[b]; + for (int j = 19; j >= 0; --j) { + if ((1 << j) & depth_difference) { + a = jmp[a][j]; + } + } + if (a == b) { + return a; + } else { + for (int j = 19; j >= 0; --j) { + if (jmp[a][j] != jmp[b][j]) { + a = jmp[a][j]; + b = jmp[b][j]; + } + } + return jmp[a][0]; + } +} + +int main() { + int n, q; + + cin >> n >> q; + + for (int i = 1; i < n; ++i) { + cin >> jmp[i][0]; + jmp[i][0]--; + g[jmp[i][0]].push_back(i); + } + + for (int j = 0; (1 << j) <= n; ++j) { + for (int i = 0; i < n; ++i) { + jmp[i][j + 1] = jmp[jmp[i][j]][j]; + } + } + + dfs(0); + + for (int qi = 0; qi < q; ++qi) { + int a, b; + cin >> a >> b; + a--; + b--; + cout << lca(a, b) + 1 << '\n'; + } +} \ No newline at end of file diff --git a/test/solution/matrix.cpp b/test/solution/matrix.cpp new file mode 100644 index 0000000..b77e45d --- /dev/null +++ b/test/solution/matrix.cpp @@ -0,0 +1,111 @@ +// https://leetcode.com/problems/number-of-zigzag-arrays-ii/solutions/7229933/matrix-exponentiation-intuition-from-q3-video-included-codeforces-blog/ + +#include +using namespace std; + +const int MOD = 1e9 + 7; + +// Codeforces Matrix Expo template (Errichto) +struct Matrix { + vector> m; + int sz; + Matrix(int n, bool ident = false) : sz(n) { + m.assign(n, vector(n, 0)); + if (ident) { + for (int i = 0; i < n; ++i) + m[i][i] = 1; + } + } + Matrix operator*(const Matrix &o) const { + Matrix res(sz); + for (int i = 0; i < sz; ++i) { + for (int k = 0; k < sz; ++k) { + if (!m[i][k]) + continue; + long long val = m[i][k]; + for (int j = 0; j < sz; ++j) { + res.m[i][j] = (res.m[i][j] + val * o.m[k][j]) % MOD; + } + } + } + return res; + } +}; + +Matrix mpow(Matrix base, long long exp) { + Matrix res(base.sz, true); + while (exp > 0) { + if (exp & 1) + res = res * base; + base = base * base; + exp >>= 1; + } + return res; +} + +class Solution { +public: + int zigZagArrays(long long n, int l, int r) { + + auto now = make_tuple(n, l, r); + + int m = r - l + 1; // number of distinct values + int S = 2 * m; // total states (value, direction) + + auto id = [&](int val, int dir) { return (val - l) * 2 + dir; }; + + // Build adjacency matrix + Matrix A(S); + + for (int val = l; val <= r; ++val) { + for (int dir = 0; dir < 2; ++dir) { + int u = id(val, dir); + if (dir == 0) { + // last was decreasing => next must be greater + for (int nxt = val + 1; nxt <= r; ++nxt) { + int v = id(nxt, 1); + A.m[u][v] = 1; + } + } else { + // last was increasing => next must be smaller + for (int nxt = l; nxt < val; ++nxt) { + int v = id(nxt, 0); + A.m[u][v] = 1; + } + } + } + } + + vector V0(S, 1); + if (n == 1) { + long long ans = 0; + for (int x : V0) + ans = (ans + x) % MOD; + return ans; + } + + Matrix An = mpow(A, n - 1); + + vector V(S, 0); + for (int j = 0; j < S; ++j) { + long long sum = 0; + for (int i = 0; i < S; ++i) { + sum += 1LL * V0[i] * An.m[i][j]; + if (sum >= 8LL * MOD) + sum %= MOD; + } + V[j] = sum % MOD; + } + + long long ans = 0; + for (auto x : V) + ans = (ans + x) % MOD; + return ans; + } +}; + +int main() { + int n, l, r; + cin >> n >> l >> r; + cout << Solution().zigZagArrays(n, l, r) << '\n'; +} \ No newline at end of file diff --git a/test/solution/mo_array.cpp b/test/solution/mo_array.cpp new file mode 100644 index 0000000..213a520 --- /dev/null +++ b/test/solution/mo_array.cpp @@ -0,0 +1,43 @@ +// https://codeforces.com/contest/86/submission/175816188 + +#include +using namespace std; +const int B = 400; +long long now, ans[200001], cnt[1000001]; +int l, r, n, m, i, a[200001]; +struct node { + int l, r, p; + int operator<(node u) { + return l / B == u.l / B ? l / B & 1 ? r < u.r : u.r < r : l < u.l; + } +} q[200001]; +void add(int x, int y) { + now -= cnt[x] * cnt[x] * x; + cnt[x] += y; + now += cnt[x] * cnt[x] * x; +} +int main() { + ios::sync_with_stdio(0); + cin.tie(0); + cin >> n >> m; + for (i = 1; i <= n; i++) + cin >> a[i]; + for (i = 1; i <= m; i++) + cin >> q[i].l >> q[i].r, q[i].p = i; + sort(q + 1, q + m + 1); + l = 1; + r = 0; + for (i = 1; i <= m; i++) { + while (l > q[i].l) + add(a[--l], 1); + while (r < q[i].r) + add(a[++r], 1); + while (l < q[i].l) + add(a[l++], -1); + while (r > q[i].r) + add(a[r--], -1); + ans[q[i].p] = now; + } + for (i = 1; i <= m; i++) + cout << ans[i] << "\n"; +} \ No newline at end of file diff --git a/test/solution/rolling_hash.cpp b/test/solution/rolling_hash.cpp new file mode 100644 index 0000000..d26cec9 --- /dev/null +++ b/test/solution/rolling_hash.cpp @@ -0,0 +1,68 @@ +// https://cses.fi/problemset/model/2106/ + +#include +#include +#include +#include +#include +using namespace std; +using ll = long long; + +mt19937 rng(chrono::steady_clock::now().time_since_epoch().count()); + +struct HashString { + const ll M = 1111111111111111111; // M is a prime. + const ll C = uniform_int_distribution(0.1 * M, 0.9 * M)(rng); + + ll mul(ll a, ll b) { return __int128(a) * b % M; } + + int n; + vector pows, sums; + + HashString(string s) : n(s.size()), pows(n + 1, 1), sums(n + 1) { + for (int i = 1; i <= n; i++) { + pows[i] = mul(pows[i - 1], C); + sums[i] = (mul(sums[i - 1], C) + s[i - 1]) % M; + } + } + + // Returns the hash of the substring [l, r) + ll hash(int l, int r) { + ll h = sums[r] - mul(sums[l], pows[r - l]); + return (h % M + M) % M; + } +}; + +int main() { + string s; + cin >> s; + int n = s.size(); + HashString hs(s); + + auto find = [&](int len) { + set hashes; + for (int i = 0; i <= n - len; i++) { + ll h = hs.hash(i, i + len); + if (hashes.count(h)) + return i; + hashes.insert(h); + } + return -1; + }; + + int len = 0; + int first = 0; + for (int b = 1 << 16; b >= 1; b /= 2) { + int pos = find(len + b); + if (pos == -1) + continue; + first = pos; + len += b; + } + + if (len == 0) { + cout << "-1\n"; + } else { + cout << s.substr(first, len) << "\n"; + } +} \ No newline at end of file diff --git a/test/solution/segment_tree.cpp b/test/solution/segment_tree.cpp new file mode 100644 index 0000000..46a7383 --- /dev/null +++ b/test/solution/segment_tree.cpp @@ -0,0 +1,67 @@ +// https://cses.fi/problemset/model/3226/ + +#include +using namespace std; +using ll = long long; +const int N = 1 << 18; + +struct Node { + ll total_sum, max_left, max_right, max_sum; +}; + +Node tree[2 * N]; + +Node combine(Node left, Node right) { + Node result; + result.total_sum = left.total_sum + right.total_sum; + result.max_left = max(left.max_left, left.total_sum + right.max_left); + result.max_right = max(right.max_right, left.max_right + right.total_sum); + result.max_sum = + max(max(left.max_sum, right.max_sum), left.max_right + right.max_left); + return result; +} + +void set_val(int pos, int value) { + pos += N; + tree[pos] = {value, value, value, value}; + + for (pos /= 2; pos >= 1; pos /= 2) { + tree[pos] = combine(tree[pos * 2], tree[pos * 2 + 1]); + } +} + +ll get_sum(int a, int b) { + a += N; + b += N; + + Node left = {}, right = {}; + while (a <= b) { + if (a % 2 == 1) { + left = combine(left, tree[a++]); + } + if (b % 2 == 0) { + right = combine(tree[b--], right); + } + a /= 2; + b /= 2; + } + + return combine(left, right).max_sum; +} + +int main() { + int n, q; + cin >> n >> q; + + for (int i = 1; i <= n; i++) { + int x; + cin >> x; + set_val(i, x); + } + + for (int i = 1; i <= q; i++) { + int a, b; + cin >> a >> b; + cout << get_sum(a, b) << "\n"; + } +} \ No newline at end of file diff --git a/test/solution/segment_tree_lazy.cpp b/test/solution/segment_tree_lazy.cpp new file mode 100644 index 0000000..64c50e4 --- /dev/null +++ b/test/solution/segment_tree_lazy.cpp @@ -0,0 +1,84 @@ +// https://cses.fi/problemset/model/1736/ + +#include +#include +using namespace std; +using ll = long long; + +// This lazy segment tree implementation supports the following operations: +// 1. add(l, r, x): in range [l, r), add x + 0 to the first element, +// x + 1 to the second, +// x + 2 to the third and so on +// 3. query(l, r): find the sum of values in range [l, r) +struct Tree { + int n; + vector sum; + vector lazy_a; + vector lazy_d; + + Tree(int n) : n(n), sum(n * 4), lazy_a(n * 4), lazy_d(n * 4) {} + + void add(int s, int l, int r, int ql, int qr, int x) { + if (r <= ql || qr <= l) + return; + if (ql <= l && r <= qr) { + apply(s, r - l, x + (l - ql), 1); + return; + } + push(s, r - l); + int m = (l + r) / 2; + add(s * 2 + 0, l, m, ql, qr, x); + add(s * 2 + 1, m, r, ql, qr, x); + sum[s] = sum[s * 2] + sum[s * 2 + 1]; + } + + ll query(int s, int l, int r, int ql, int qr) { + if (r <= ql || qr <= l) + return 0; + if (ql <= l && r <= qr) + return sum[s]; + push(s, r - l); + int m = (l + r) / 2; + return query(s * 2, l, m, ql, qr) + query(s * 2 + 1, m, r, ql, qr); + } + + void push(int s, int len) { + apply(s * 2 + 0, len / 2, lazy_a[s], lazy_d[s]); + apply(s * 2 + 1, (len + 1) / 2, lazy_a[s] + len / 2 * lazy_d[s], lazy_d[s]); + lazy_a[s] = 0; + lazy_d[s] = 0; + } + + void apply(int s, int len, ll a, ll d) { + sum[s] += a * len + d * ll(len - 1) * len / 2; + lazy_a[s] += a; + lazy_d[s] += d; + } + + void add(int l, int r, int x) { add(1, 0, n, l, r, x); } + ll query(int l, int r) { return query(1, 0, n, l, r); } +}; + +int main() { + int n, q; + cin >> n >> q; + + Tree tree(n); + + for (int i = 0; i < n; ++i) { + int t; + cin >> t; + tree.add(i, i + 1, t); + } + + for (int qi = 0; qi < q; ++qi) { + int t, l, r; + cin >> t >> l >> r; + l--; + if (t == 1) { + tree.add(l, r, 1); + } else { + cout << tree.query(l, r) << '\n'; + } + } +} \ No newline at end of file diff --git a/test/solution/sparse_table.cpp b/test/solution/sparse_table.cpp new file mode 100644 index 0000000..a214e1c --- /dev/null +++ b/test/solution/sparse_table.cpp @@ -0,0 +1,42 @@ +// https://cses.fi/problemset/model/1647/ + +#include +using namespace std; + +const int N = 200000; +const int L = 20; + +int x[N]; +int table[N][L]; + +// Returns the minimum value in range [l, r) +int get_min(int l, int r) { + // __lg(x) returns the base-2 logarithm of x, rounded down. + // With it, we'll figure out the largest power of two + // that is not larger than the length of the query. + int i = __lg(r - l); + return min(table[l][i], table[r - (1 << i)][i]); +} + +int main() { + int n, q; + cin >> n >> q; + + for (int i = 0; i < n; ++i) { + cin >> x[i]; + table[i][0] = x[i]; + } + + for (int j = 0; (1 << j) < n; ++j) { + for (int i = 0; i + (1 << j) < n; ++i) { + table[i][j + 1] = min(table[i][j], table[i + (1 << j)][j]); + } + } + + for (int qi = 0; qi < q; ++qi) { + int l, r; + cin >> l >> r; + l--; + cout << get_min(l, r) << '\n'; + } +} \ No newline at end of file diff --git a/test/solution/strongly_connected_components.cpp b/test/solution/strongly_connected_components.cpp new file mode 100644 index 0000000..d7c978c --- /dev/null +++ b/test/solution/strongly_connected_components.cpp @@ -0,0 +1,70 @@ +// https://cses.fi/problemset/model/1683/ + +#include +#include +#include + +using namespace std; + +vector> graph; +vector> graph_rev; + +vector order; +vector seen; + +void find_order(int node) { + if (seen[node]) + return; + seen[node] = 1; + for (auto next_node : graph[node]) { + find_order(next_node); + } + order.push_back(node); +} + +vector component; + +void find_component(int node, int id) { + if (component[node]) + return; + component[node] = id; + for (auto next_node : graph_rev[node]) { + find_component(next_node, id); + } +} + +int main() { + int n, m; + cin >> n >> m; + + graph.resize(n + 1); + graph_rev.resize(n + 1); + + for (int i = 1; i <= m; i++) { + int a, b; + cin >> a >> b; + graph[a].push_back(b); + graph_rev[b].push_back(a); + } + + seen.resize(n + 1); + for (int i = 1; i <= n; i++) { + find_order(i); + } + + reverse(order.begin(), order.end()); + int count = 0; + component.resize(n + 1); + for (auto node : order) { + if (component[node]) + continue; + count++; + find_component(node, count); + } + + cout << count << "\n"; + for (int i = 1; i <= n; i++) { + cout << component[i] << " "; + } + cout << "\n"; +} \ No newline at end of file diff --git a/test/solution/treap.cpp b/test/solution/treap.cpp new file mode 100644 index 0000000..4b5ebeb --- /dev/null +++ b/test/solution/treap.cpp @@ -0,0 +1,103 @@ +// https://cses.fi/problemset/model/2072/ + +#include +#include +#include +#include +using namespace std; + +mt19937 rng(chrono::high_resolution_clock::now().time_since_epoch().count()); + +struct Treap { + Treap *left = nullptr, *right = nullptr; + int v, weight, sz = 1; + Treap() {} + Treap(int v) : v(v), weight(rng()) {} + + static void update(Treap *node) { + node->sz = 1; + if (node->left) + node->sz += node->left->sz; + if (node->right) + node->sz += node->right->sz; + } + + // Merges the treaps a and b. + static Treap *merge(Treap *a, Treap *b) { + if (!a) + return b; + if (!b) + return a; + Treap *r; + if (a->weight < b->weight) { + a->right = merge(a->right, b); + r = a; + } else { + b->left = merge(a, b->left); + r = b; + } + update(r); + return r; + } + + // Splits the treap into two treaps. + // The first of the returned treaps will contain the k first nodes of a. + static pair split(Treap *a, int k) { + if (!a) + return {nullptr, nullptr}; + int al = a->left ? a->left->sz : 0; + Treap *r; + if (al >= k) { + tie(r, a->left) = split(a->left, k); + update(a); + return {r, a}; + } + tie(a->right, r) = split(a->right, k - al - 1); + update(a); + return {a, r}; + } + + static void print(Treap *a, char endl = '\n') { + if (a->left) + print(a->left, '\0'); + cout << (char)a->v; + if (a->right) + print(a->right, '\0'); + if (endl) + cout << endl; + } +}; + +Treap *new_treap(int v) { + const int pool_sz = 2e5; + static Treap pool[pool_sz]; + static Treap *pool_ptr = pool; + + *pool_ptr = Treap(v); + return pool_ptr++; +} + +int main() { + Treap *root = nullptr; + + int n, m; + cin >> n >> m; + + string s; + cin >> s; + + for (char c : s) { + root = Treap::merge(root, new_treap(c)); + } + + for (int i = 0; i < m; ++i) { + int a, b; + cin >> a >> b; + auto [r0, r1] = Treap::split(root, a - 1); + auto [r2, r3] = Treap::split(r1, b - a + 1); + root = Treap::merge(r0, r3); + root = Treap::merge(root, r2); + } + + Treap::print(root); +} \ No newline at end of file diff --git a/test/solution/union_find.cpp b/test/solution/union_find.cpp new file mode 100644 index 0000000..0f02019 --- /dev/null +++ b/test/solution/union_find.cpp @@ -0,0 +1,52 @@ +// https://cses.fi/problemset/model/1676/ + +#include +#include +#include + +using namespace std; +const int N = 100001; + +int sz[N]; +int link[N]; + +int find(int x) { + if (link[x] == x) { + return x; + } + return link[x] = find(link[x]); +} + +bool same(int x, int y) { return find(x) == find(y); } + +void unite(int x, int y) { + x = find(x); + y = find(y); + if (sz[x] < sz[y]) { + swap(x, y); + } + sz[x] += sz[y]; + link[y] = x; +} + +int main() { + int n, m; + cin >> n >> m; + + fill(sz, sz + n + 1, 1); + iota(link, link + n + 1, 0); + + int n_components = n; + int largest_size = 1; + + for (int i = 0; i < m; ++i) { + int a, b; + cin >> a >> b; + if (!same(a, b)) { + unite(a, b); + --n_components; + largest_size = max(sz[find(a)], largest_size); + } + cout << n_components << ' ' << largest_size << '\n'; + } +} \ No newline at end of file