├── Other ├── readme.md ├── Custom Hash │ ├── readme.md │ └── custom_hash.cpp ├── Annealing Optimization │ ├── readme.md │ └── annealing.cpp ├── Parallel Binary Search │ ├── readme.md │ └── binary_search.cpp └── Long Long Multiplication │ ├── readme.md │ └── mul.cpp ├── Strings ├── readme.md ├── Knuth Morris Pratt │ ├── readme.md │ └── knuth_morris_pratt.cpp ├── Manacher │ ├── readme.md │ └── manacher.cpp ├── Z-Function │ ├── z_function.cpp │ └── readme.md └── Suffix Array │ └── suffix_array.cpp ├── Geometry ├── readme.md └── Convex Hull │ ├── readme.md │ └── graham.cpp ├── Sorting ├── readme.md └── Radix Sort │ ├── readme.md │ └── radix_sort.cpp ├── Combinatorics ├── readme.md ├── Binary Pow │ ├── readme.md │ └── binary_pow.cpp ├── Generating Functions │ ├── generating_functions2.cpp │ ├── readme.md │ └── generating_functions1.cpp ├── Partitions Of N │ ├── readme.md │ └── partitions_of_n.cpp ├── Binomial Coefficient │ ├── readme.md │ ├── cnk1.cpp │ └── cnk2.cpp ├── Lagrange Interpolation │ ├── readme.md │ ├── lagrange1.cpp │ └── lagrange2.cpp ├── Lucas │ ├── readme.md │ └── lucas.cpp ├── Chinese Remainder Theorem │ ├── readme.md │ └── chinese_remainder_theorem.cpp ├── Fast Fourier Transform │ ├── readme.md │ └── fast_fourier_transform.cpp └── Binary Pow Matrices │ ├── readme.md │ └── binary_pow_matrices.cpp ├── Editorials └── IZhO │ └── 2017 │ ├── Bomb │ └── readme.md │ ├── Longest beautiful sequence │ └── readme.md │ ├── Simple game │ ├── readme.md │ └── english.md │ ├── Money │ ├── readme.md │ └── english.md │ ├── Hard route │ ├── readme.md │ └── english.md │ └── Bootfall │ ├── readme.md │ └── english.md ├── Graph Theory ├── readme.md ├── 2-SAT │ └── two-sat.cpp ├── Cut Vertex │ ├── readme.md │ └── cut_vertex.cpp ├── Floyd Warshall │ ├── readme.md │ └── floyd_warshall.cpp ├── Centroid Decomposition │ ├── readme.md │ └── centroid_decomposition.cpp ├── Tree Isomorphism │ ├── readme.md │ └── isomorphism.cpp ├── Dijkstra │ ├── readme.md │ ├── dijkstra2.cpp │ └── dijkstra1.cpp ├── Edmonds Karp │ ├── readme.md │ └── edmonds_karp.cpp ├── Ford Fulkerson │ ├── readme.md │ └── ford_fulkerson.cpp ├── Kruskal │ ├── readme.md │ └── kruskal.cpp ├── Prim │ ├── readme.md │ ├── prim2.cpp │ └── prim1.cpp ├── DFS and BFS │ ├── readme.md │ ├── dfs.cpp │ └── bfs.cpp ├── Bridges │ ├── readme.md │ ├── bridges1.cpp │ └── bridges2.cpp ├── Least Common Ancestor │ ├── readme.md │ ├── lca1.cpp │ └── lca2.cpp ├── Kuhn │ ├── readme.md │ ├── kuhn.cpp │ └── fast_kuhn.cpp ├── Ford Bellman │ ├── readme.md │ └── ford_bellman.cpp ├── Boruvka │ ├── readme.md │ └── boruvka.cpp ├── Disjoint Set Union │ ├── dsu.cpp │ ├── dsu_tree.cpp │ ├── readme-kaz.md │ └── readme.md └── Heavy Light Decomposition │ ├── readme.md │ ├── node_hld.cpp │ └── edge_hld.cpp ├── Number Theory ├── readme.md ├── Greatest Common Divisor │ ├── readme.md │ └── gcd.cpp └── Modular Inverse │ ├── readme.md │ └── inverse.cpp ├── .gitignore ├── Data Structures ├── readme.md ├── Faster Hash Table │ ├── readme.md │ └── faster_hash_table.cpp ├── Persistent Segment Tree │ ├── readme.md │ └── persistent_segment_tree.cpp ├── Heap │ ├── readme.md │ └── heap.cpp ├── Binomial Heap │ ├── readme.md │ └── binomial_heap.cpp ├── Mo │ ├── readme.md │ └── mo.cpp ├── Ordered Set │ ├── readme.md │ └── ordered_set.cpp ├── Fenwick Tree │ ├── readme.md │ ├── fenwick_tree1.cpp │ └── fenwick_tree2.cpp ├── Cartesian Tree │ ├── readme.md │ ├── cartesian_tree2.cpp │ └── cartesian_tree1.cpp ├── Historic Segment Tree │ ├── readme.md │ └── historic_segment_tree.cpp ├── Segment Tree Beats │ ├── readme.md │ └── segment_tree_beats.cpp ├── AVL Tree │ ├── readme.md │ └── avl_tree.cpp ├── Sqrt Decomposition │ ├── readme.md │ ├── sqrt_decomposition1.cpp │ └── sqrt_decomposition2.cpp ├── Sparse Table │ ├── sparse_table1.cpp │ ├── readme.md │ └── sparse_table2.cpp └── Segment Tree │ ├── segment_tree3.cpp │ ├── readme.md │ ├── segment_tree1.cpp │ └── segment_tree2.cpp ├── Dynamic Programming ├── readme.md ├── Convex Hull Trick │ ├── images │ │ ├── convex_hull_1.png │ │ ├── convex_hull_2.png │ │ ├── convex_hull_3.png │ │ ├── convex_hull_4.png │ │ ├── convex_hull_5.png │ │ ├── convex_hull_6.png │ │ ├── convex_hull_7.png │ │ └── convex_hull_8.png │ └── readme.md ├── Slope Trick │ ├── readme.md │ └── slope_trick.cpp ├── Li Chao Tree │ ├── readme.md │ └── li_chao_tree.cpp └── Meet in the middle on DP │ └── readme.md └── readme.md /Other/readme.md: -------------------------------------------------------------------------------- 1 | # Разное -------------------------------------------------------------------------------- /Strings/readme.md: -------------------------------------------------------------------------------- 1 | # Строки -------------------------------------------------------------------------------- /Geometry/readme.md: -------------------------------------------------------------------------------- 1 | # Геометрия -------------------------------------------------------------------------------- /Sorting/readme.md: -------------------------------------------------------------------------------- 1 | # Сортировка -------------------------------------------------------------------------------- /Combinatorics/readme.md: -------------------------------------------------------------------------------- 1 | # Комбинаторика -------------------------------------------------------------------------------- /Editorials/IZhO/2017/Bomb/readme.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Graph Theory/readme.md: -------------------------------------------------------------------------------- 1 | # Теория графов -------------------------------------------------------------------------------- /Number Theory/readme.md: -------------------------------------------------------------------------------- 1 | # Теория чисел -------------------------------------------------------------------------------- /Other/Custom Hash/readme.md: -------------------------------------------------------------------------------- 1 | Custom hash -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/*.afdesign* 2 | **/*.html* -------------------------------------------------------------------------------- /Data Structures/readme.md: -------------------------------------------------------------------------------- 1 | # Структуры данных -------------------------------------------------------------------------------- /Graph Theory/2-SAT/two-sat.cpp: -------------------------------------------------------------------------------- 1 | write 2 sat 2 | -------------------------------------------------------------------------------- /Other/Annealing Optimization/readme.md: -------------------------------------------------------------------------------- 1 | Метод отжига -------------------------------------------------------------------------------- /Editorials/IZhO/2017/Longest beautiful sequence/readme.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Data Structures/Faster Hash Table/readme.md: -------------------------------------------------------------------------------- 1 | Faster hash table -------------------------------------------------------------------------------- /Dynamic Programming/readme.md: -------------------------------------------------------------------------------- 1 | # Динамическое программирование -------------------------------------------------------------------------------- /Graph Theory/Cut Vertex/readme.md: -------------------------------------------------------------------------------- 1 | Поиск точек сочленения $O(N+M)$ -------------------------------------------------------------------------------- /Graph Theory/Floyd Warshall/readme.md: -------------------------------------------------------------------------------- 1 | Алгоритм Флойда-Уоршелла $O(N^3)$ -------------------------------------------------------------------------------- /Strings/Knuth Morris Pratt/readme.md: -------------------------------------------------------------------------------- 1 | Алгоритм Кнута-Морриса-Пратта $O(N)$ -------------------------------------------------------------------------------- /Geometry/Convex Hull/readme.md: -------------------------------------------------------------------------------- 1 | Алгоритм поиска выпуклой оболочки $O(NlogN)$ -------------------------------------------------------------------------------- /Combinatorics/Binary Pow/readme.md: -------------------------------------------------------------------------------- 1 | Бинарное возведение числа $a$ в степень $N$ за $O(logN)$. -------------------------------------------------------------------------------- /Combinatorics/Generating Functions/generating_functions2.cpp: -------------------------------------------------------------------------------- 1 | // https://github.com/bekzhan29/algos -------------------------------------------------------------------------------- /Combinatorics/Partitions Of N/readme.md: -------------------------------------------------------------------------------- 1 | Количество разбиений числа $N$ на слагаемые $O(N\sqrt{N})$ -------------------------------------------------------------------------------- /Other/Parallel Binary Search/readme.md: -------------------------------------------------------------------------------- 1 | [Задача](https://atcoder.jp/contests/agc002/tasks/agc002_d) -------------------------------------------------------------------------------- /Graph Theory/Centroid Decomposition/readme.md: -------------------------------------------------------------------------------- 1 | [Задача](https://atcoder.jp/contests/abc291/tasks/abc291_h) -------------------------------------------------------------------------------- /Other/Long Long Multiplication/readme.md: -------------------------------------------------------------------------------- 1 | Перемножение двух чисел до $10^{18}$ по модулю третьего числа до $10^{18}$. -------------------------------------------------------------------------------- /Number Theory/Greatest Common Divisor/readme.md: -------------------------------------------------------------------------------- 1 | Наибольший общий делитель чисел $a$ и $b$. 2 | 3 | Время работы $O(logN)$. -------------------------------------------------------------------------------- /Combinatorics/Binomial Coefficient/readme.md: -------------------------------------------------------------------------------- 1 | Биномиальные коэффициенты. 2 | 1. `cnk1.cpp` $O(N^2)$. 3 | 2. `cnk2.cpp` $O(N)$. -------------------------------------------------------------------------------- /Graph Theory/Tree Isomorphism/readme.md: -------------------------------------------------------------------------------- 1 | Изоморфизм деревьев $O(NlogN)$. 2 | 3 | [Задача](https://cses.fi/problemset/task/1700/) -------------------------------------------------------------------------------- /Strings/Manacher/readme.md: -------------------------------------------------------------------------------- 1 | Алгоритм Манакера $O(N)$ 2 | $d1$ - палиндромы нечетной длины 3 | $d2$ - палиндромы четной длины -------------------------------------------------------------------------------- /Sorting/Radix Sort/readme.md: -------------------------------------------------------------------------------- 1 | Цифровая сортировка 2 | Время работы $O(Nlog_{base}(A))$, где $A$ максимальное число в массиве. 3 | -------------------------------------------------------------------------------- /Number Theory/Greatest Common Divisor/gcd.cpp: -------------------------------------------------------------------------------- 1 | int gcd(int a, int b) 2 | { 3 | if (!b) 4 | return a; 5 | return gcd(b, a % b); 6 | } -------------------------------------------------------------------------------- /Combinatorics/Lagrange Interpolation/readme.md: -------------------------------------------------------------------------------- 1 | Интерполяция Лагранжа. 2 | 1. `lagrange1.cpp` $O(N^2logN)$ 3 | 2. `lagrange2.cpp` $O(NlogN)$. -------------------------------------------------------------------------------- /Data Structures/Persistent Segment Tree/readme.md: -------------------------------------------------------------------------------- 1 | Персистентное дерево отрезков 2 | 1. $a[x]$ += $y$ $O(logN)$ 3 | 2. $sum(l, r)$ в версии $i$ $O(logN)$ -------------------------------------------------------------------------------- /Graph Theory/Dijkstra/readme.md: -------------------------------------------------------------------------------- 1 | Алгоритм Дейкстры находит кратчайшее расстояние от одной вершины до всех остальных за время $O(MlogN)$ или $O(M+N^2)$. -------------------------------------------------------------------------------- /Graph Theory/Edmonds Karp/readme.md: -------------------------------------------------------------------------------- 1 | [Задача](https://www.hackerearth.com/practice/algorithms/graphs/maximum-flow/practice-problems/algorithm/find-the-flow) -------------------------------------------------------------------------------- /Graph Theory/Ford Fulkerson/readme.md: -------------------------------------------------------------------------------- 1 | [Задача](https://www.hackerearth.com/practice/algorithms/graphs/maximum-flow/practice-problems/algorithm/find-the-flow) -------------------------------------------------------------------------------- /Dynamic Programming/Convex Hull Trick/images/convex_hull_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekzhan29/algos/HEAD/Dynamic Programming/Convex Hull Trick/images/convex_hull_1.png -------------------------------------------------------------------------------- /Dynamic Programming/Convex Hull Trick/images/convex_hull_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekzhan29/algos/HEAD/Dynamic Programming/Convex Hull Trick/images/convex_hull_2.png -------------------------------------------------------------------------------- /Dynamic Programming/Convex Hull Trick/images/convex_hull_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekzhan29/algos/HEAD/Dynamic Programming/Convex Hull Trick/images/convex_hull_3.png -------------------------------------------------------------------------------- /Dynamic Programming/Convex Hull Trick/images/convex_hull_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekzhan29/algos/HEAD/Dynamic Programming/Convex Hull Trick/images/convex_hull_4.png -------------------------------------------------------------------------------- /Dynamic Programming/Convex Hull Trick/images/convex_hull_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekzhan29/algos/HEAD/Dynamic Programming/Convex Hull Trick/images/convex_hull_5.png -------------------------------------------------------------------------------- /Dynamic Programming/Convex Hull Trick/images/convex_hull_6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekzhan29/algos/HEAD/Dynamic Programming/Convex Hull Trick/images/convex_hull_6.png -------------------------------------------------------------------------------- /Dynamic Programming/Convex Hull Trick/images/convex_hull_7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekzhan29/algos/HEAD/Dynamic Programming/Convex Hull Trick/images/convex_hull_7.png -------------------------------------------------------------------------------- /Dynamic Programming/Convex Hull Trick/images/convex_hull_8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekzhan29/algos/HEAD/Dynamic Programming/Convex Hull Trick/images/convex_hull_8.png -------------------------------------------------------------------------------- /Data Structures/Heap/readme.md: -------------------------------------------------------------------------------- 1 | Пирамида поддерживает следующие запросы 2 | 1. Добавить элемент $O(logN)$ 3 | 2. Найти минимальный элемент $O(1)$ 4 | 3. Удалить минимальный элемент $O(logN)$ -------------------------------------------------------------------------------- /Dynamic Programming/Slope Trick/readme.md: -------------------------------------------------------------------------------- 1 | Slope Trick $O(NlogN)$ 2 | 3 | [Задача](https://codeforces.com/contest/713/problem/C) 4 | 5 | [Пост](https://codeforces.com/blog/entry/77298) -------------------------------------------------------------------------------- /Data Structures/Binomial Heap/readme.md: -------------------------------------------------------------------------------- 1 | Биномиальная пирамида 2 | 1. Объединить 2 пирамиды $O(logN)$ 3 | 2. Найти минимум $O(logN)$ 4 | 3. Добавить элемент $O(logN)$ 5 | 4. Удалить минимум $O(logN)$ -------------------------------------------------------------------------------- /Data Structures/Faster Hash Table/faster_hash_table.cpp: -------------------------------------------------------------------------------- 1 | // https://github.com/bekzhan29/algos 2 | #include 3 | using namespace __gnu_pbds; 4 | gp_hash_table table; -------------------------------------------------------------------------------- /Data Structures/Mo/readme.md: -------------------------------------------------------------------------------- 1 | Алгоритм Мо это корневая оптимизация помогающая ответить на сложные запросы на отрезке за $O((N+Q)\sqrt{N})$. 2 | 3 | [Задача](https://atcoder.jp/contests/abc293/tasks/abc293_g) -------------------------------------------------------------------------------- /Graph Theory/Kruskal/readme.md: -------------------------------------------------------------------------------- 1 | Алгоритм Крускала находит минимальное оставное дерево в связном неориентированном взвешенном графе за время $O(MlogM)$. 2 | 3 | [Задача](https://www.spoj.com/problems/MST/) -------------------------------------------------------------------------------- /Graph Theory/Prim/readme.md: -------------------------------------------------------------------------------- 1 | Алгоритм Прима находит минимальное оставное дерево в связном неориентированном взвешенном графе за время $O(MlogN)$ или $O(M+N^2)$. 2 | 3 | [Задача](https://www.spoj.com/problems/MST/) -------------------------------------------------------------------------------- /Graph Theory/DFS and BFS/readme.md: -------------------------------------------------------------------------------- 1 |
2 | dfs.cpp 3 | Обход в глубину $O(N+M)$ 4 |
5 |
6 | bfs.cpp 7 | Обход в ширину $O(N+M)$ 8 |
-------------------------------------------------------------------------------- /Dynamic Programming/Li Chao Tree/readme.md: -------------------------------------------------------------------------------- 1 | Li Chao Tree $O(NlogN)$ 2 | 1. Добавить новую прямую $O(logN)$ 3 | 2. Найти ответ $O(logN)$ 4 | 5 | [Пост](https://cp-algorithms.com/geometry/convex_hull_trick.html#li-chao-tree) на английском -------------------------------------------------------------------------------- /Other/Long Long Multiplication/mul.cpp: -------------------------------------------------------------------------------- 1 | ll mul(ll a, ll b, ll m) 2 | { 3 | // https://github.com/bekzhan29/algos 4 | ll q = (ll)((long double)a * (long double)b / (long double)m); 5 | ll r = a * b - q * m; 6 | return (r + 5 * m) % m; 7 | } -------------------------------------------------------------------------------- /Dynamic Programming/Meet in the middle on DP/readme.md: -------------------------------------------------------------------------------- 1 | Задачи 2 | 3 | 1. [Hamming Distances](https://csacademy.com/contest/round-67/task/hamming-distances/) 4 | 2. [Longest beautiful sequence](https://oj.uz/problem/view/IZhO17_subsequence) 5 | -------------------------------------------------------------------------------- /Combinatorics/Lucas/readme.md: -------------------------------------------------------------------------------- 1 | Теорема Лукаса позволяет считать $C_{N}^K$ % $P$ для больших $N$ и $K$ и маленького простого $P$. 2 | 3 | Предпосчет $O(P)$. 4 | 5 | Ответ на запрос $O(logN)$. 6 | 7 | [Задача](https://www.codechef.com/problems/CHKSEL) -------------------------------------------------------------------------------- /Combinatorics/Chinese Remainder Theorem/readme.md: -------------------------------------------------------------------------------- 1 | Китайская теорема об остатках $O(N)$ 2 | 3 | Найти такое $x$, что $x$ % $k[i] = re[i]$ для всех $1 \le i \le sz$ 4 | Все $k[i]$ взаимнопросты 5 | $m=k[1] * k[2] * ... * k[sz]$ 6 | $0 \le x < m$ 7 | -------------------------------------------------------------------------------- /Graph Theory/Bridges/readme.md: -------------------------------------------------------------------------------- 1 |
2 | bridges1.cpp 3 | Поиск мостов без кратных рёбер $O(N+M)$ 4 |
5 |
6 | bridges2.cpp 7 | Поиск мостов с кратными рёбрами $O(N+M)$ 8 |
-------------------------------------------------------------------------------- /Combinatorics/Binary Pow/binary_pow.cpp: -------------------------------------------------------------------------------- 1 | int bin_pow(int a, int n, int m) 2 | { 3 | // https://github.com/bekzhan29/algos 4 | int ans = 1; 5 | while (n) 6 | { 7 | if (n & 1) 8 | ans = 1LL * ans * a % m; 9 | a = 1LL * a * a % m; 10 | n /= 2; 11 | } 12 | return ans; 13 | } -------------------------------------------------------------------------------- /Graph Theory/Least Common Ancestor/readme.md: -------------------------------------------------------------------------------- 1 | Наименьший общий предок 2 |
3 | lca1.cpp 4 | Метод двоичного подъема $O(logN)$ 5 |
6 |
7 | lca2.cpp 8 | Поиск деревом отрезков $O(logN)$ 9 |
-------------------------------------------------------------------------------- /Data Structures/Ordered Set/readme.md: -------------------------------------------------------------------------------- 1 | Ordered set позволяет делать все то же самое что и set, но при этом умеет отвечать на 2 вида запросов 2 | 1. `s.find_by_order(k)` - найти элемент на позиции $k$ $O(logN)$. 3 | 2. `s.order_of_key(x)` - найти номер элемента $x$ $O(logN)$. 4 | 5 | Обе операции 0 индексированы 6 | -------------------------------------------------------------------------------- /Number Theory/Modular Inverse/readme.md: -------------------------------------------------------------------------------- 1 | Находит обратный элемент от $a$ по модулю $m$ за время $O(\sqrt{M}+logM)$. 2 | Если заранее известно что $m$ простое, то $phi(m) = m-1$ и время работы будет $O(logM)$. 3 | Обратный элемент по модулю существует только тогда, когда $a$ и $m$ взаимнопросты. То есть $gcd(a, m) = 1$. -------------------------------------------------------------------------------- /Data Structures/Ordered Set/ordered_set.cpp: -------------------------------------------------------------------------------- 1 | // https://github.com/bekzhan29/algos 2 | #include 3 | #include 4 | 5 | using namespace __gnu_pbds; 6 | 7 | typedef tree, rb_tree_tag, tree_order_statistics_node_update> ordered_set; 8 | 9 | ordered_set s; -------------------------------------------------------------------------------- /Combinatorics/Fast Fourier Transform/readme.md: -------------------------------------------------------------------------------- 1 | Быстрое Преобразование Фурье $O(NlogN)$ 2 | 3 | [Задача 1](https://atcoder.jp/contests/abc291/tasks/abc291_g) 4 | 5 | [Задача 2](https://codeforces.com/contest/827/problem/E) 6 | [Разбор](https://www.youtube.com/watch?v=z7J-x3RxwIo) задачи 2 от [Um_nik](https://codeforces.com/profile/Um_nik) -------------------------------------------------------------------------------- /Graph Theory/Kuhn/readme.md: -------------------------------------------------------------------------------- 1 | Алгоритм Куна находит паросочетание в двудольном графе за $O(NM)$. 2 | 3 | Так же предоставлен алгоритм Куна с оптимизацией `fast_kuhn.cpp`, который работает для $N\le10^5$. Но точная асимптотика неизвестна. 4 | 5 | задача на минимальное вершинное покрытие https://csacademy.com/contest/archive/task/no-prime-sum/ 6 | -------------------------------------------------------------------------------- /Strings/Knuth Morris Pratt/knuth_morris_pratt.cpp: -------------------------------------------------------------------------------- 1 | ll n, p[N]; 2 | string s; 3 | void build() 4 | { 5 | // https://github.com/bekzhan29/algos 6 | n = s.size(); 7 | for (ll i = 1; i < n; i++) 8 | { 9 | ll j = p[i - 1]; 10 | while (j > 0 && s[j] != s[i]) 11 | j = p[j - 1]; 12 | if (s[i] == s[j]) 13 | j++; 14 | p[i] = j; 15 | } 16 | } -------------------------------------------------------------------------------- /Data Structures/Fenwick Tree/readme.md: -------------------------------------------------------------------------------- 1 | [Видео](https://www.youtube.com/watch?v=5aPjt7WF8oY&t=1620s) урок от [Нияза Нигматуллина](https://codeforces.com/profile/niyaznigmatul) 2 | Таймкод `fenwickTree1.cpp` [27:00-1:00:00](https://www.youtube.com/watch?v=5aPjt7WF8oY&t=1620s) 3 | Таймкод `fenwickTree2.cpp` [1:01:00-1:30:00](https://www.youtube.com/watch?v=5aPjt7WF8oY&t=3660s) -------------------------------------------------------------------------------- /Graph Theory/Ford Bellman/readme.md: -------------------------------------------------------------------------------- 1 | Алгоритм Форда-Беллмана находит кратчайшее расстояние от одной вершины до всех остальных за время $O(NM)$. 2 | 3 | Данная реализация содержит оптимизацию. Если на одной из итераций не удалось сократить путь до какой-либо вершины, то алгоритм завершается. В худшем случае алгоритм будет работать $O(NM)$, но в рандомных графах время работы сократится. -------------------------------------------------------------------------------- /Data Structures/Cartesian Tree/readme.md: -------------------------------------------------------------------------------- 1 | Введение. [Пост. 1 часть](https://habr.com/ru/post/101818/) 2 | 3 | Операции в декартовом дереве. [Пост. 2 часть](https://habr.com/ru/post/102006/) 4 | 5 | Декартово дерево по неявному ключу. [Пост. 3 часть](https://habr.com/ru/post/102364/) 6 | 7 | 1. `cartesian_tree1.cpp` Декартово дерево. 8 | 2. `cartesian_tree2.cpp` Декартово дерево по неявному ключу. -------------------------------------------------------------------------------- /Graph Theory/Boruvka/readme.md: -------------------------------------------------------------------------------- 1 | Алгоритм Борувки находит минимальное оставное дерево в связном неориентированном взвешенном графе за время $O(MlogN)$. 2 | $e[N]$ - ребра 3 | $mst[N]$ - ребра в минимальном оставном дереве 4 | 5 | [Видео](https://www.youtube.com/watch?v=nMabN7SrHIU) урок от [Жана Курбанбаева](https://codeforces.com/profile/rockyb) 6 | 7 | [Задача](https://www.spoj.com/problems/MST/) -------------------------------------------------------------------------------- /Data Structures/Historic Segment Tree/readme.md: -------------------------------------------------------------------------------- 1 | Historic segment tree $O(NlogN)$ 2 | 1. $a[i]$ += $x$ на отрезке $l,r$ 3 | 2. $a[i]=max(a[i]-x,0)$ на отрезке $l,r$ 4 | 3. $a[i]=x$ на отрезке $l,r$ 5 | 4. Вывести $a[i]$ 6 | 5. Вывести $b[i]$ 7 | 8 | $b[i]$ = максимальное значение $a[i]$ за все время 9 | [Задача](https://uoj.ac/problem/164) 10 | [Пост](https://codeforces.com/blog/entry/57319) 11 | -------------------------------------------------------------------------------- /Data Structures/Segment Tree Beats/readme.md: -------------------------------------------------------------------------------- 1 | Segment tree beats $O(Nlog^2N)$ 2 | 1. Вывести сумму на отрезке $l,r$ 3 | 2. Взять по модулю на отрезке $l,r$ $a[i]$ %= $x$ 4 | 3. Обновление $a[i]=x$ 5 | 6 | [Видео](https://www.youtube.com/watch?v=nMabN7SrHIU) урок от [Errichto](https://codeforces.com/profile/errichto) 7 | 8 | [Видео](https://www.youtube.com/watch?v=_TGNYtkWAsQ) урок от [SecondThread](https://codeforces.com/profile/SecondThread) -------------------------------------------------------------------------------- /Combinatorics/Generating Functions/readme.md: -------------------------------------------------------------------------------- 1 | Производящая функция 2 |
3 | generating_functions1.cpp 4 | 5 | [Atcoder Beginner 285 Ex](https://atcoder.jp/contests/abc285/tasks/abc285_h) 6 | 7 |
8 |
9 | generating_functions2.cpp 10 | 11 | [USACO 2019 December Platinum Tree Depth](http://usaco.org/index.php?page=viewproblem2&cpid=974) 12 |
13 | -------------------------------------------------------------------------------- /Strings/Z-Function/z_function.cpp: -------------------------------------------------------------------------------- 1 | vector z_function(string s) 2 | { 3 | // https://github.com/bekzhan29/algos 4 | int n = s.size(); 5 | vector z(n); 6 | for (int i = 1, l = 0, r = 0; i < n; i++) 7 | { 8 | if (i <= r) 9 | z[i] = min(r - i + 1, z[i - l]); 10 | while (i + z[i] < n && s[z[i]] == s[i + z[i]]) 11 | z[i]++; 12 | if (i + z[i] - 1 > r) 13 | { 14 | l = i; 15 | r = i + z[i] - 1; 16 | } 17 | } 18 | return z; 19 | } -------------------------------------------------------------------------------- /Data Structures/AVL Tree/readme.md: -------------------------------------------------------------------------------- 1 | АВЛ дерево. 2 | 3 | 1. Добавить элемент $O(logN)$. 4 | 2. Удалить элемент $O(logN)$. 5 | 3. Узнать минимальный элемент $O(logN)$. 6 | 4. Узнать размер $O(1)$. 7 | 5. Узнать высоту $O(1)$. 8 | 6. Узнать пустое ли дерево $O(1)$. 9 | 10 | [Видео](https://www.youtube.com/watch?v=t-8X-Jnyv3o&t=1284s) урок от [Нияза Нигматуллина](https://codeforces.com/profile/niyaznigmatul) 11 | Таймкод [21:24-38:50](https://www.youtube.com/watch?v=t-8X-Jnyv3o&t=1284s) 12 | -------------------------------------------------------------------------------- /Graph Theory/Bridges/bridges1.cpp: -------------------------------------------------------------------------------- 1 | ll cnt, w[N], dep[N], up[N]; 2 | pair br[N]; 3 | vector v[N]; 4 | void dfs(ll x, ll pr, ll depth) 5 | { 6 | // https://github.com/bekzhan29/algos 7 | dep[x] = up[x] = depth; 8 | w[x] = 1; 9 | for (ll to : v[x]) 10 | { 11 | if (to == pr) 12 | continue; 13 | if (!w[to]) 14 | { 15 | dfs(to, x, depth + 1); 16 | up[x] = min(up[x], up[to]); 17 | if (up[to] > dep[x]) 18 | br[++cnt] = {x, to}; 19 | } 20 | else 21 | up[x] = min(up[x], dep[to]); 22 | } 23 | } -------------------------------------------------------------------------------- /Other/Custom Hash/custom_hash.cpp: -------------------------------------------------------------------------------- 1 | struct custom_hash 2 | { 3 | // https://github.com/bekzhan29/algos 4 | static uint64_t splitmix64(uint64_t x) 5 | { 6 | x += 0x9e3779b97f4a7c15; 7 | x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9; 8 | x = (x ^ (x >> 27)) * 0x94d049bb133111eb; 9 | return x ^ (x >> 31); 10 | } 11 | 12 | size_t operator()(uint64_t x) const 13 | { 14 | static const uint64_t FIXED_RANDOM = chrono::steady_clock::now().time_since_epoch().count(); 15 | return splitmix64(x + FIXED_RANDOM); 16 | } 17 | }; -------------------------------------------------------------------------------- /Data Structures/Sqrt Decomposition/readme.md: -------------------------------------------------------------------------------- 1 | Корневая оптимизация умеет делать всё то, что умеет делать дерево отрезков и даже больше. Но время обработки запросов обычно $O(\sqrt{N})$, что хуже чем $O(logN)$. 2 | 3 |
4 | sqrt_decomposition1.cpp 5 | 6 | 1. $a[x]$ += $y$ $O(1)$ 7 | 2. $sum(l,r)$ $O(\sqrt{N})$ 8 |
9 |
10 | sqrt_decomposition2.cpp 11 | 12 | 1. $upd(l,r)$ += $k$ $O(\sqrt{N})$ 13 | 2. $sum(l,r)$ $O(\sqrt{N})$ 14 |
15 | -------------------------------------------------------------------------------- /Data Structures/Sparse Table/sparse_table1.cpp: -------------------------------------------------------------------------------- 1 | ll log2[N], sp[20][N]; 2 | void build() 3 | { 4 | log2[1] = 0; 5 | for (ll i = 2; i <= n; i++) 6 | log2[i] = log2[i / 2] + 1; 7 | for (ll i = 1; i <= n; i++) 8 | sp[0][i] = a[i]; 9 | for (ll j = 1; j <= log2[n]; j++) 10 | for (ll i = 1; i <= n - (1 << j) + 1; i++) 11 | sp[j][i] = min(sp[j - 1][i], sp[j - 1][i + (1 << (j - 1))]); 12 | } 13 | ll get_min(ll l, ll r) 14 | { 15 | // https://github.com/bekzhan29/algos 16 | ll k = log2[r - l + 1]; 17 | return min(sp[k][l], sp[k][r - (1 << k) + 1]); 18 | } -------------------------------------------------------------------------------- /Graph Theory/Floyd Warshall/floyd_warshall.cpp: -------------------------------------------------------------------------------- 1 | ll x[N * N], y[N * N], z[N * N], dist[N][N]; 2 | void floydWarshall() 3 | { 4 | // https://github.com/bekzhan29/algos 5 | for (ll i = 1; i <= n; i++) 6 | for (ll j = 1; j <= n; j++) 7 | dist[i][j] = INF; 8 | for (ll i = 1; i <= n; i++) 9 | dist[i][i] = 0; 10 | for (ll i = 1; i <= m; i++) 11 | dist[x[i]][y[i]] = z[i], 12 | dist[y[i]][x[i]] = z[i]; 13 | for (ll k = 1; k <= n; k++) 14 | for (ll i = 1; i <= n; i++) 15 | for (ll j = 1; j <= n; j++) 16 | dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]); 17 | } -------------------------------------------------------------------------------- /Graph Theory/Cut Vertex/cut_vertex.cpp: -------------------------------------------------------------------------------- 1 | ll cnt, w[N], dep[N], up[N], cut[N]; 2 | vector v[N]; 3 | void dfs(ll x, ll pr, ll depth) 4 | { 5 | // https://github.com/bekzhan29/algos 6 | dep[x] = up[x] = depth; 7 | w[x] = 1; 8 | ll ch = 0, go = 0; 9 | for (ll to : v[x]) 10 | { 11 | if (to == pr) 12 | continue; 13 | if (!w[to]) 14 | { 15 | go++; 16 | dfs(to, x, depth + 1); 17 | up[x] = min(up[x], up[to]); 18 | if (up[to] >= dep[x]) 19 | ch = 1; 20 | } 21 | else 22 | up[x] = min(up[x], dep[to]); 23 | } 24 | if (pr == 0) 25 | ch = go > 1; 26 | if (ch) 27 | cut[++cnt] = x; 28 | } -------------------------------------------------------------------------------- /Combinatorics/Binary Pow Matrices/readme.md: -------------------------------------------------------------------------------- 1 | Возводит матрицу $A$ размера $N*N$ в степень $K$ за время $O(N^3logK)$. 2 | 3 | [Пост](https://codeforces.com/blog/entry/80195) от [Errichto](https://codeforces.com/profile/Errichto) 4 | 5 | [Задачи](https://codeforces.com/gym/102644) 6 | 7 | [AtCoder Beginner Contest 293 - E](https://atcoder.jp/contests/abc293/tasks/abc293_e) 8 | 9 |
10 | 11 | Raises matrix $A$ of size $N*N$ to the power of $K$ in $O(N^3logK)$ time. 12 | 13 | [Blog](https://codeforces.com/blog/entry/80195) by [Errichto](https://codeforces.com/profile/Errichto) 14 | 15 | [Tasks](https://codeforces.com/gym/102644) 16 | -------------------------------------------------------------------------------- /Graph Theory/Disjoint Set Union/dsu.cpp: -------------------------------------------------------------------------------- 1 | struct dsu 2 | { 3 | vector p, r; 4 | int find_root(int a) 5 | { 6 | // https://github.com/bekzhan29/algos 7 | if (a == p[a]) 8 | return a; 9 | return p[a] = find_root(p[a]); 10 | } 11 | bool union_sets(int a, int b) 12 | { 13 | a = find_root(a), b = find_root(b); 14 | if (a == b) 15 | return 0; 16 | if (r[a] > r[b]) 17 | r[a] += r[b], p[b] = a; 18 | else 19 | r[b] += r[a], p[a] = b; 20 | return 1; 21 | } 22 | void init(int n) 23 | { 24 | p.resize(n + 1); 25 | r.resize(n + 1); 26 | for (int i = 1; i <= n; i++) 27 | p[i] = i, r[i] = 1; 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /Graph Theory/Bridges/bridges2.cpp: -------------------------------------------------------------------------------- 1 | struct edge 2 | { 3 | ll x, y, ind; 4 | edge(ll x = 0, ll y = 0, ll ind = 0) : x(x), y(y), ind(ind) {} 5 | } br[N]; 6 | ll cnt, w[N], dep[N], up[N]; 7 | vector v[N]; 8 | void dfs(ll x, ll last, ll depth) 9 | { 10 | // https://github.com/bekzhan29/algos 11 | dep[x] = up[x] = depth; 12 | w[x] = 1; 13 | for (edge e : v[x]) 14 | { 15 | if (e.ind == last) 16 | continue; 17 | ll to = e.y; 18 | if (!w[to]) 19 | { 20 | dfs(to, e.ind, depth + 1); 21 | up[x] = min(up[x], up[to]); 22 | if (up[to] > dep[x]) 23 | br[++cnt] = e; 24 | } 25 | else 26 | up[x] = min(up[x], dep[to]); 27 | } 28 | } -------------------------------------------------------------------------------- /Graph Theory/DFS and BFS/dfs.cpp: -------------------------------------------------------------------------------- 1 | struct graph 2 | { 3 | // https://github.com/bekzhan29/algos 4 | int n; 5 | vector> g; 6 | vector used; 7 | vector d; 8 | graph(int n) 9 | { 10 | this->n = n; 11 | g = vector>(n, vector(0)); 12 | used = vector(n, false); 13 | d = vector(n, -1); 14 | } 15 | void add_edge(int v, int u, bool directional) 16 | { 17 | g[v].push_back(u); 18 | if (!directional) 19 | g[u].push_back(v); 20 | } 21 | void dfs(int v, int p = -1) 22 | { 23 | used[v] = true; 24 | for (int to : g[v]) 25 | { 26 | if (!used[to]) 27 | dfs(to, v); 28 | } 29 | } 30 | }; -------------------------------------------------------------------------------- /Graph Theory/Dijkstra/dijkstra2.cpp: -------------------------------------------------------------------------------- 1 | ll dist[N], w[N]; 2 | struct edge 3 | { 4 | ll x, y, w; 5 | edge(ll x = 0, ll y = 0, ll w = 0) : x(x), y(y), w(w) {} 6 | }; 7 | vector v[N]; 8 | void dijkstra(ll st, ll n) 9 | { 10 | // https://github.com/bekzhan29/algos 11 | for (ll i = 1; i <= n; i++) 12 | dist[i] = INF, w[i] = 0; 13 | dist[st] = 0; 14 | for (;;) 15 | { 16 | ll x = 0; 17 | for (ll i = 1; i <= n; i++) 18 | if (w[i] == 0 && (x == 0 || dist[i] < dist[x])) 19 | x = i; 20 | if (x == 0) 21 | break; 22 | w[x] = 1; 23 | for (edge e : v[x]) 24 | if (dist[e.x] + e.w < dist[e.y]) 25 | dist[e.y] = dist[e.x] + e.w; 26 | } 27 | } -------------------------------------------------------------------------------- /Graph Theory/Prim/prim2.cpp: -------------------------------------------------------------------------------- 1 | ll dist[N], w[N], pr[N]; 2 | struct edge 3 | { 4 | ll x, y, w; 5 | edge(ll x = 0, ll y = 0, ll w = 0) : x(x), y(y), w(w) {} 6 | }; 7 | vector v[N]; 8 | void prim(ll n) 9 | { 10 | // https://github.com/bekzhan29/algos 11 | for (ll i = 1; i <= n; i++) 12 | dist[i] = INF, w[i] = 0; 13 | dist[1] = pr[1] = 0; 14 | for (;;) 15 | { 16 | ll x = 0; 17 | for (ll i = 1; i <= n; i++) 18 | if (w[i] == 0 && (x == 0 || dist[i] < dist[x])) 19 | x = i; 20 | if (x == 0) 21 | break; 22 | w[x] = 1; 23 | for (edge e : v[x]) 24 | if (!w[e.y] && e.w < dist[e.y]) 25 | dist[e.y] = e.w, pr[e.y] = e.x; 26 | } 27 | } -------------------------------------------------------------------------------- /Graph Theory/Prim/prim1.cpp: -------------------------------------------------------------------------------- 1 | ll dist[N], w[N], pr[N]; 2 | struct edge 3 | { 4 | ll x, y, w; 5 | edge(ll x = 0, ll y = 0, ll w = 0) : x(x), y(y), w(w) {} 6 | }; 7 | vector v[N]; 8 | void prim(ll n) 9 | { 10 | // https://github.com/bekzhan29/algos 11 | priority_queue> s; 12 | for (ll i = 1; i <= n; i++) 13 | dist[i] = INF, w[i] = 0; 14 | dist[1] = 0; 15 | s.push({0, 1}); 16 | for (; !s.empty();) 17 | { 18 | ll x = s.top().se; 19 | s.pop(); 20 | if (w[x]) 21 | continue; 22 | w[x] = 1; 23 | for (edge e : v[x]) 24 | if (!w[e.y] && e.w < dist[e.y]) 25 | dist[e.y] = e.w, pr[e.y] = e.x, s.push({-dist[e.y], e.y}); 26 | } 27 | } -------------------------------------------------------------------------------- /Graph Theory/Dijkstra/dijkstra1.cpp: -------------------------------------------------------------------------------- 1 | ll dist[N], w[N]; 2 | struct edge 3 | { 4 | ll x, y, w; 5 | edge(ll x = 0, ll y = 0, ll w = 0) : x(x), y(y), w(w) {} 6 | }; 7 | vector v[N]; 8 | void dijkstra(ll st, ll n) 9 | { 10 | // https://github.com/bekzhan29/algos 11 | priority_queue> s; 12 | for (ll i = 1; i <= n; i++) 13 | dist[i] = INF, w[i] = 0; 14 | dist[st] = 0; 15 | s.push({0, st}); 16 | for (; !s.empty();) 17 | { 18 | ll x = s.top().se; 19 | s.pop(); 20 | if (w[x]) 21 | continue; 22 | w[x] = 1; 23 | for (edge e : v[x]) 24 | if (dist[e.x] + e.w < dist[e.y]) 25 | dist[e.y] = dist[e.x] + e.w, s.push({-dist[e.y], e.y}); 26 | } 27 | } -------------------------------------------------------------------------------- /Graph Theory/Ford Bellman/ford_bellman.cpp: -------------------------------------------------------------------------------- 1 | ll dist[N]; 2 | struct edge 3 | { 4 | ll x, y, w; 5 | edge(ll x = 0, ll y = 0, ll w = 0) : x(x), y(y), w(w) {} 6 | } e[N]; 7 | void ford_bellman(int st, int n, int m) 8 | { 9 | // https://github.com/bekzhan29/algos 10 | for (int i = 1; i <= n; i++) 11 | dist[i] = INF; 12 | dist[st] = 0; 13 | for (int i = 1; i <= n; i++) 14 | { 15 | bool optimized = false; 16 | for (int j = 1; j <= m; j++) 17 | if (dist[e.y] > dist[e.x] + e.w) 18 | { 19 | optimized = true; 20 | dist[e.y] = dist[e.x] + e.w; 21 | if (i == n) 22 | cout << "Negative weight cycle" 23 | } 24 | if (!optimized) 25 | break; 26 | } 27 | } -------------------------------------------------------------------------------- /Graph Theory/Kuhn/kuhn.cpp: -------------------------------------------------------------------------------- 1 | ll mu[N], go[N], w[N]; 2 | vector v[N]; 3 | ll try_kuhn(ll x) 4 | { 5 | if (w[x]) 6 | return 0; 7 | w[x] = 1; 8 | for (ll to : v[x]) 9 | if (!mu[to]) 10 | { 11 | mu[to] = x; 12 | go[x] = to; 13 | return 1; 14 | } 15 | for (ll to : v[x]) 16 | if (try_kuhn(mu[to])) 17 | { 18 | mu[to] = x; 19 | go[x] = to; 20 | return 1; 21 | } 22 | return 0; 23 | } 24 | void kuhn() 25 | { 26 | // https://github.com/bekzhan29/algos 27 | ll ans = 0; 28 | for (ll i = 1; i <= n; i++) 29 | mu[i] = go[i] = 0; 30 | for (ll i = 1; i <= n; i++) 31 | { 32 | for (ll j = 1; j <= n; j++) 33 | w[j] = 0; 34 | ans += try_kuhn(i); 35 | } 36 | } -------------------------------------------------------------------------------- /Editorials/IZhO/2017/Simple game/readme.md: -------------------------------------------------------------------------------- 1 | # Простая игра 2 | Давайте создадим массив $ans[H]$, который будет хранить количество пересечений для горизонтальной прямой на высоте $H$. Тогда чтобы ответить на второй тип запроса надо просто вывести $ans[H]$. 3 | 4 | Посмотрим на два соседних элемента $h[i]$ и $h[i+1]$. Без потери общности предположим что $h[i] \le h[i+1]$. Какие прямые пересекут отрезок между $(i, h[i])$ и $(i+1, h[i+1])$? Все прямые $H$, такие что $h[i] \le H \le h[i+1]$. Тогда добавим $1$ на позициях от $h[i]$ до $h[i+1]$ в массиве $ans$. Сделать это можно с помощью дерева отрезков. Запросы первого типа обрабатываются похожим образом. 5 | 6 | Время работы $O((N+M)logH+H)$ где $H$ максимальное значение $h[i]$. -------------------------------------------------------------------------------- /Strings/Z-Function/readme.md: -------------------------------------------------------------------------------- 1 |
2 | Описание 3 | Для строки $S$, возвращает массив длины $|S|$ где $i$-й элемент равен наибольшему числу символов начинающихся в позиции $i$ и равному префиксу $S$. 4 | 5 | Асимптотика $O(|S|)$. 6 | 7 | Ссылка на пост: https://codeforces.com/blog/entry/3107 8 |
9 | 10 |
11 | Description 12 | For a string $S$, returns an array of length $|S|$ where the $i$-th element is equal to the 13 | greatest number of characters starting from the position $i$ that coincide with the 14 | first characters of $S$. 15 | 16 | Complexity is $O(|S|)$. 17 | 18 | Tutorial link: https://codeforces.com/blog/entry/3107 19 |
20 | -------------------------------------------------------------------------------- /Combinatorics/Binomial Coefficient/cnk1.cpp: -------------------------------------------------------------------------------- 1 | template 2 | struct cnk 3 | { 4 | // https://github.com/bekzhan29/algos 5 | private: 6 | int n; 7 | vector> a; 8 | 9 | public: 10 | void init_cnk(int n) 11 | { 12 | this->n = n; 13 | a.resize(n + 1); 14 | for (int i = 0; i <= n; i++) 15 | a[i].resize(n + 1); 16 | a[0][0] = 1; 17 | for (int i = 1; i <= n; i++) 18 | for (int j = 0; j <= i; j++) 19 | { 20 | a[i][j] = a[i - 1][j]; 21 | if (j) 22 | a[i][j] += a[i - 1][j - 1]; 23 | if (a[i][j] >= mod) 24 | a[i][j] -= mod; 25 | } 26 | } 27 | Type c(int n, int k) 28 | { 29 | if (k > n || k < 0 || n < 0) 30 | return 0; 31 | return a[n][k]; 32 | } 33 | }; -------------------------------------------------------------------------------- /Editorials/IZhO/2017/Simple game/english.md: -------------------------------------------------------------------------------- 1 | # Simple game 2 | Let's create an array $ans[H]$ that will store the number of intersections for a horizontal line at height $H$. Then to answer the second type of query, we just need to print $ans[H]$. 3 | 4 | Let's look at two adjacent elements $h[i]$ and $h[i+1]$. Without loss of generality, assume that $h[i] \le h[i+1]$. Which lines will intersect the segment between $(i, h[i])$ and $(i+1, h[i+1])$? All lines $H$ such that $h[i] \le H \le h[i+1]$. Then add $1$ at positions from $h[i]$ to $h[i+1]$ in the array $ans$. This can be done using a segment tree. Queries of the first type are processed in a similar way. 5 | 6 | The running time is $O((N+M)logH+H)$ where $H$ is the maximum value of $h[i]$. -------------------------------------------------------------------------------- /Data Structures/Fenwick Tree/fenwick_tree1.cpp: -------------------------------------------------------------------------------- 1 | template 2 | struct fenwick 3 | { 4 | // https://github.com/bekzhan29/algos 5 | private: 6 | int n; 7 | Type tree[N]; 8 | Type sum(int pos) 9 | { 10 | int ans = 0; 11 | for (; pos >= 0; pos = (pos & (pos + 1)) - 1) 12 | ans += tree[pos]; 13 | return ans; 14 | } 15 | 16 | public: 17 | void build(int n, Type *a) 18 | { 19 | this->n = n; 20 | for (int i = 0; i <= n; i++) 21 | tree[i] = 0; 22 | for (int i = 1; i <= n; i++) 23 | add(i, a[i]); 24 | } 25 | void add(int pos, Type x) 26 | { 27 | for (; pos <= n; pos = (pos | (pos + 1))) 28 | tree[pos] += x; 29 | } 30 | Type sum(int l, int r) 31 | { 32 | return sum(r) - sum(l - 1); 33 | } 34 | }; -------------------------------------------------------------------------------- /Number Theory/Modular Inverse/inverse.cpp: -------------------------------------------------------------------------------- 1 | int bin_pow(int a, int n, int m) 2 | { 3 | // https://github.com/bekzhan29/algos 4 | int ans = 1; 5 | while (n) 6 | { 7 | if (n & 1) 8 | ans = 1LL * ans * a % m; 9 | a = 1LL * a * a % m; 10 | n /= 2; 11 | } 12 | return ans; 13 | } 14 | int phi(int n) 15 | { 16 | int ans = n; 17 | for (int i = 2; i * i <= n; i++) 18 | if (n % i == 0) 19 | { 20 | while (n % i == 0) 21 | n /= i; 22 | ans -= ans / i; 23 | } 24 | if (n > 1) 25 | ans -= ans / n; 26 | return ans; 27 | } 28 | // m не простое 29 | int inverse(int a, int m) 30 | { 31 | return bin_pow(a, phi(m) - 1, m); 32 | } 33 | // m простое 34 | int inverse_prime(int a, int m) 35 | { 36 | return bin_pow(a, m - 2, m); 37 | } -------------------------------------------------------------------------------- /Graph Theory/Heavy Light Decomposition/readme.md: -------------------------------------------------------------------------------- 1 | Heavy-Light Decomposition 2 | 1. $a[x]$ = $y$ $O(logN)$. 3 | 2. $max(u,v)$ $O(log^2(N))$. 4 | 5 |
6 | Пример использования node_hld.cpp 7 | 8 | ```cpp 9 | hld h; 10 | // Инициализировать для h все нужные массивы длины n 11 | h.init(n); 12 | // Добавить ребро между вершиной 1 и 2 13 | h.add_edge(1, 2); 14 | // Построить структуру по массиву a 15 | h.build(a); 16 | // Обновить вес вершины 1 на 5 17 | h.upd(1, 5); 18 | // Вывести максимальный вес вершины на пути от 1 до n 19 | cout << h.get(1, n); 20 | ``` 21 |
22 | 23 | [Задача 1](https://atcoder.jp/contests/abc294/tasks/abc294_g) 24 | [Задача 2](https://acm.timus.ru/problem.aspx?space=1&num=1553) 25 | -------------------------------------------------------------------------------- /Sorting/Radix Sort/radix_sort.cpp: -------------------------------------------------------------------------------- 1 | void radix_sort(int *a, int l, int r, int base = 10) 2 | { 3 | // https://github.com/bekzhan29/algos 4 | if (l >= r) 5 | return; 6 | vector> b, c[base]; 7 | int mx = a[l]; 8 | for (int i = l; i <= r; i++) 9 | { 10 | b.push_back({a[i], a[i]}); 11 | mx = max(mx, a[i]); 12 | } 13 | int len = 0; 14 | while (mx) 15 | { 16 | mx /= base; 17 | len++; 18 | } 19 | for (int k = 1; k <= len; k++) 20 | { 21 | for (auto x : b) 22 | c[x.fi % base].push_back({x.fi / base, x.se}); 23 | b.clear(); 24 | for (int i = 0; i < base; i++) 25 | { 26 | for (auto x : c[i]) 27 | b.push_back(x); 28 | c[i].clear(); 29 | } 30 | } 31 | for (int i = l; i <= r; i++) 32 | a[i] = b[i - l].se; 33 | } -------------------------------------------------------------------------------- /Graph Theory/Kuhn/fast_kuhn.cpp: -------------------------------------------------------------------------------- 1 | ll mu[N], go[N], w[N]; 2 | vector v[N]; 3 | ll try_kuhn(ll x) 4 | { 5 | if (w[x]) 6 | return 0; 7 | w[x] = 1; 8 | for (ll to : v[x]) 9 | if (!mu[to]) 10 | { 11 | mu[to] = x; 12 | go[x] = to; 13 | return 1; 14 | } 15 | for (ll to : v[x]) 16 | if (try_kuhn(mu[to])) 17 | { 18 | mu[to] = x; 19 | go[x] = to; 20 | return 1; 21 | } 22 | return 0; 23 | } 24 | void kuhn() 25 | { 26 | // https://github.com/bekzhan29/algos 27 | ll ans = 0; 28 | for (ll i = 1; i <= n; i++) 29 | mu[i] = go[i] = 0; 30 | for (ll ch = 1; ch;) 31 | { 32 | ch = 0; 33 | for (ll i = 1; i <= n; i++) 34 | w[i] = 0; 35 | for (ll i = 1; i <= n; i++) 36 | if (go[i] == 0 && try_kuhn(i)) 37 | ans++, ch = 1; 38 | } 39 | } -------------------------------------------------------------------------------- /Graph Theory/Least Common Ancestor/lca1.cpp: -------------------------------------------------------------------------------- 1 | const ll lg = 17; 2 | ll pr[lg][N], tin[N], tout[N], tim; 3 | vector v[N]; 4 | void dfs(ll x, ll par) 5 | { 6 | // https://github.com/bekzhan29/algos 7 | tin[x] = ++tim; 8 | pr[0][x] = par; 9 | if (par == 0) 10 | pr[0][x] = x; 11 | for (ll i = 1; i < lg; i++) 12 | pr[i][x] = pr[i - 1][pr[i - 1][x]]; 13 | for (ll to : v[x]) 14 | if (to != par) 15 | dfs(to, x); 16 | tout[x] = tim; 17 | } 18 | ll lca(ll x, ll y) 19 | { 20 | ll ans = x; 21 | for (ll i = lg - 1; i >= 0; i--) 22 | if (tin[pr[i][ans]] > tin[y] || tout[y] > tout[pr[i][ans]]) 23 | ans = pr[i][lca]; 24 | if (tin[ans] > tin[y] || tout[y] > tout[ans]) 25 | ans = pr[0][ans]; 26 | return ans; 27 | } 28 | void init(ll root) 29 | { 30 | dfs(root, 0); 31 | } -------------------------------------------------------------------------------- /Data Structures/Sparse Table/readme.md: -------------------------------------------------------------------------------- 1 | [Видео](https://www.youtube.com/watch?v=5aPjt7WF8oY&t=0s) урок для `sparse_table2.cpp` от [Нияза Нигматуллина](https://codeforces.com/profile/niyaznigmatul) 2 | Таймкод [0:00-27:00](https://www.youtube.com/watch?v=5aPjt7WF8oY&t=0s) 3 | 4 |
5 | sparse_table1.cpp 6 | 7 | 1. Построение $O(NlogN)$ 8 | 2. $min(l,r)$ $O(1)$ 9 |
10 |
11 | sparse_table2.cpp 12 | 13 | 1. Построение $O(NlogN)$ 14 | 2. $sum(l,r)$ $O(1)$ 15 | 16 | Данная реализация работает для таких запросов как посчитать перемножение на отрезке по любому модулю, даже не простому. Сделать это с помощью sparse_table1.cpp невозможно. 17 | 18 | Так же можно считать перемножение матриц на отрезке. 19 |
-------------------------------------------------------------------------------- /Strings/Manacher/manacher.cpp: -------------------------------------------------------------------------------- 1 | ll n, d1[N], d2[N], l, r; 2 | string s; 3 | void manacher() 4 | { 5 | // https://github.com/bekzhan29/algos 6 | n = s.size(); 7 | l = r = 0; 8 | for (ll i = 1; i <= n; i++) 9 | { 10 | d1[i] = 1; 11 | if (r >= i) 12 | d1[i] = min(d1[l + r - i], r - i + 1); 13 | while (i + d1[i] <= n && i + d1[i] >= 1 && s[i + d1[i] - 1] == s[i - d1[i] - 1]) 14 | d1[i]++; 15 | if (r < i + d1[i] - 1) 16 | r = i + d1[i] - 1, l = i - d1[i] + 1; 17 | } 18 | l = r = 0; 19 | for (ll i = 1; i <= n; i++) 20 | { 21 | d2[i] = 0; 22 | if (r >= i) 23 | d2[i] = min(d2[l + r - i + 1], r - i + 1); 24 | while (i + d2[i] <= n && i - d2[i] - 1 >= 1 && s[i + d2[i] - 1] == s[i - d2[i] - 2]) 25 | d2[i]++; 26 | if (r < i + d2[i] - 1) 27 | r = i + d2[i] - 1, l = i - d2[i]; 28 | } 29 | } -------------------------------------------------------------------------------- /Graph Theory/DFS and BFS/bfs.cpp: -------------------------------------------------------------------------------- 1 | struct graph 2 | { 3 | // https://github.com/bekzhan29/algos 4 | int n; 5 | vector> g; 6 | vector used; 7 | vector d; 8 | graph(int n) 9 | { 10 | this->n = n; 11 | g = vector>(n, vector(0)); 12 | used = vector(n, false); 13 | d = vector(n, -1); 14 | } 15 | void add_edge(int v, int u, bool directional) 16 | { 17 | g[v].push_back(u); 18 | if (!directional) 19 | g[u].push_back(v); 20 | } 21 | void bfs(int start) 22 | { 23 | queue q; 24 | q.push(start); 25 | d[start] = 0; 26 | while (!q.empty()) 27 | { 28 | int v = q.front(); 29 | q.pop(); 30 | for (int to : g[v]) 31 | { 32 | if (d[to] == -1) 33 | { 34 | d[to] = d[v] + 1; 35 | q.push(to); 36 | } 37 | } 38 | } 39 | } 40 | }; -------------------------------------------------------------------------------- /Dynamic Programming/Slope Trick/slope_trick.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | #define pb push_back 4 | #define mp make_pair 5 | #define INF ll(2e9) 6 | #define mod 998244353 7 | #define eps 1e-9 8 | #define abs(x) ((x) >= 0 ? (x) : -(x)) 9 | #define y1 solai 10 | #define fi first 11 | #define se second 12 | typedef long long ll; 13 | typedef long double ld; 14 | typedef pair pll; 15 | typedef pair pdd; 16 | const ll N = 100100; 17 | ll n, x, ans; 18 | priority_queue q; 19 | int main() 20 | { 21 | // https://github.com/bekzhan29/algos 22 | ios_base::sync_with_stdio(0); 23 | cin.tie(0); 24 | 25 | cin >> n; 26 | for (ll i = 1; i <= n; i++) 27 | { 28 | cin >> x, x -= i; 29 | q.push(x); 30 | q.push(x); 31 | ans += q.top() - x; 32 | q.pop(); 33 | } 34 | cout << ans; 35 | } -------------------------------------------------------------------------------- /Data Structures/Sparse Table/sparse_table2.cpp: -------------------------------------------------------------------------------- 1 | ll n1, lg2[2 * N], sp[20][2 * N]; 2 | void build() 3 | { 4 | n1 = 1; 5 | while (n1 <= n) 6 | n1 *= 2; 7 | n1--; 8 | lg2[1] = 0; 9 | for (ll i = 2; i <= n1; i++) 10 | lg2[i] = lg2[i / 2] + 1; 11 | for (ll i = 1; i <= n; i++) 12 | sp[0][i] = a[i]; 13 | for (ll j = 1; j <= lg2[n] + 1; j++) 14 | for (ll i = 0; i < n1; i += (1 << j)) 15 | { 16 | ll mid = i + (1 << (j - 1)), r = min(i + (1 << j) - 1, n), sum = 0; 17 | for (ll k = mid; k <= r; k++) 18 | sum += a[k], sp[j][k] = sum; 19 | sum = 0; 20 | for (ll k = mid - 1; k >= i; k--) 21 | sum += a[k], sp[j][k] = sum; 22 | } 23 | } 24 | ll sum(ll l, ll r) 25 | { 26 | // https://github.com/bekzhan29/algos 27 | ll k = lg2[r ^ l] + 1; 28 | if (l == r) 29 | return sp[0][l]; 30 | return sp[k][l] + sp[k][r]; 31 | } -------------------------------------------------------------------------------- /Combinatorics/Chinese Remainder Theorem/chinese_remainder_theorem.cpp: -------------------------------------------------------------------------------- 1 | ll sz, k[N], mi[N], rmi[N], re[N]; 2 | ll bin(ll a, ll n, ll m) 3 | { 4 | ll ans = 1; 5 | while (n) 6 | { 7 | if (n & 1) 8 | ans *= a, ans %= m; 9 | a *= a, a %= m; 10 | n /= 2; 11 | } 12 | return ans; 13 | } 14 | ll phi(ll n) 15 | { 16 | ll ans = n; 17 | for (ll i = 2; i * i <= n; i++) 18 | if (n % i == 0) 19 | { 20 | while (n % i == 0) 21 | n /= i; 22 | ans -= ans / i; 23 | } 24 | if (n > 1) 25 | ans -= ans / n; 26 | return ans; 27 | } 28 | ll CRM() 29 | { 30 | // https://github.com/bekzhan29/algos 31 | ll m = 1, ans = 0; 32 | for (ll i = 1; i <= sz; i++) 33 | m *= k[i]; 34 | for (ll i = 1; i <= sz; i++) 35 | { 36 | mi[i] = m / k[i]; 37 | rmi[i] = bin(mi[i], phi(k[i]) - 1, k[i]); 38 | ans += re[i] * rmi[i] * mi[i]; 39 | ans %= m; 40 | } 41 | return ans; 42 | } -------------------------------------------------------------------------------- /Graph Theory/Kruskal/kruskal.cpp: -------------------------------------------------------------------------------- 1 | ll p[N], r[N], msz; 2 | struct edge 3 | { 4 | ll x, y, w; 5 | edge(ll x = 0, ll y = 0, ll w = 0) : x(x), y(y), w(w) {} 6 | } e[N], mst[N]; 7 | bool cmp(edge a, edge b) 8 | { 9 | return a.w < b.w; 10 | } 11 | void init_dsu(ll n) 12 | { 13 | for (ll i = 1; i <= n; i++) 14 | p[i] = i, r[i] = 1; 15 | } 16 | ll fin(ll a) 17 | { 18 | if (a == p[a]) 19 | return a; 20 | return p[a] = fin(p[a]); 21 | } 22 | bool uni(ll a, ll b) 23 | { 24 | a = fin(a), b = fin(b); 25 | if (a == b) 26 | return 0; 27 | if (r[a] > r[b]) 28 | r[a] += r[b], p[b] = a; 29 | else 30 | r[b] += r[a], p[a] = b; 31 | return 1; 32 | } 33 | void kruskal(ll n, ll m) 34 | { 35 | // https://github.com/bekzhan29/algos 36 | init_dsu(n); 37 | msz = 0; 38 | sort(e + 1, e + m + 1, &cmp); 39 | for (ll i = 1; i <= m; i++) 40 | if (uni(e[i].x, e[i].y)) 41 | mst[++msz] = e[i]; 42 | } -------------------------------------------------------------------------------- /Combinatorics/Binomial Coefficient/cnk2.cpp: -------------------------------------------------------------------------------- 1 | int bin_pow(int a, int n, int m) 2 | { 3 | int ans = 1; 4 | while (n) 5 | { 6 | if (n & 1) 7 | ans = 1LL * ans * a % m; 8 | a = 1LL * a * a % m; 9 | n /= 2; 10 | } 11 | return ans; 12 | } 13 | template 14 | struct cnk 15 | { 16 | // https://github.com/bekzhan29/algos 17 | private: 18 | int n; 19 | vector f, rf; 20 | 21 | public: 22 | void init_cnk(int n) 23 | { 24 | this->n = n; 25 | f.resize(n + 1); 26 | rf.resize(n + 1); 27 | f[0] = 1; 28 | for (int i = 1; i <= n; i++) 29 | f[i] = 1LL * f[i - 1] * i % mod; 30 | rf[n] = bin_pow(f[n], mod - 2, mod); 31 | for (int i = n - 1; i >= 0; i--) 32 | rf[i] = 1LL * rf[i + 1] * (i + 1) % mod; 33 | } 34 | Type c(int n, int k) 35 | { 36 | if (k > n || k < 0 || n < 0) 37 | return 0; 38 | return 1LL * f[n] * rf[k] % mod * rf[n - k] % mod; 39 | } 40 | }; -------------------------------------------------------------------------------- /Graph Theory/Centroid Decomposition/centroid_decomposition.cpp: -------------------------------------------------------------------------------- 1 | ll used[N], cnt[N]; 2 | vector v[N], pr[N]; 3 | void dfs(ll x, ll par) 4 | { 5 | cnt[x] = 1; 6 | for (ll to : v[x]) 7 | { 8 | if (to == par || used[to]) 9 | continue; 10 | dfs(to, x); 11 | cnt[x] += cnt[to]; 12 | } 13 | } 14 | void dfs1(ll root, ll x, ll par) 15 | { 16 | pr[x].pb(root); 17 | for (ll to : v[x]) 18 | { 19 | if (to == par || used[to]) 20 | continue; 21 | dfs1(root, to, x); 22 | } 23 | } 24 | void cen(ll x) 25 | { 26 | // https://github.com/bekzhan29/algos 27 | dfs(x, 0); 28 | ll sz = cnt[x]; 29 | for (ll ch = 1; ch;) 30 | { 31 | ch = 0; 32 | for (ll to : v[x]) 33 | if (cnt[to] < cnt[x] && cnt[to] > sz / 2) 34 | { 35 | ch = 1; 36 | x = to; 37 | break; 38 | } 39 | } 40 | used[x] = 1; 41 | dfs1(x, x, 0); 42 | for (ll to : v[x]) 43 | if (!used[to]) 44 | cen(to); 45 | } -------------------------------------------------------------------------------- /Graph Theory/Tree Isomorphism/isomorphism.cpp: -------------------------------------------------------------------------------- 1 | int k; 2 | map, int> id; 3 | struct tree 4 | { 5 | // https://github.com/bekzhan29/algos 6 | int n, root; 7 | vector> v; 8 | tree(int n, int root = 1) 9 | { 10 | this->n = n; 11 | this->root = root; 12 | v.resize(n + 1); 13 | } 14 | void add_edge(int x, int y) 15 | { 16 | v[x].pb(y); 17 | v[y].pb(x); 18 | } 19 | int get_id(int x = -1, int pr = -1) 20 | { 21 | if (x == -1) 22 | x = root; 23 | vector a; 24 | for (int to : v[x]) 25 | { 26 | if (to == pr) 27 | continue; 28 | int to_id = get_id(to, x); 29 | a.pb(to_id); 30 | } 31 | sort(a.begin(), a.end()); 32 | if (!id.count(a)) 33 | return id[a] = ++k; 34 | return id[a]; 35 | } 36 | }; -------------------------------------------------------------------------------- /Data Structures/Sqrt Decomposition/sqrt_decomposition1.cpp: -------------------------------------------------------------------------------- 1 | template 2 | struct sqrt_dec 3 | { 4 | // https://github.com/bekzhan29/algos 5 | int len; 6 | vector a, bl; 7 | void build(int n, Type *ar) 8 | { 9 | len = sqrt(n); 10 | bl.resize(n / len + 1); 11 | a.push_back(0); 12 | for (int i = 1; i <= n; i++) 13 | { 14 | a.push_back(ar[i]); 15 | bl[i / len] += ar[i]; 16 | } 17 | } 18 | void upd(int pos, Type k) 19 | { 20 | a[pos] += k; 21 | bl[pos / len] += k; 22 | } 23 | Type sum(int l, int r) 24 | { 25 | Type ans = 0; 26 | int cl = l / len, cr = r / len; 27 | if (cl == cr) 28 | { 29 | for (int i = l; i <= r; i++) 30 | ans += a[i]; 31 | return ans; 32 | } 33 | for (int i = l; i < (cl + 1) * len; i++) 34 | ans += a[i]; 35 | for (int i = cl + 1; i < cr; i++) 36 | ans += bl[i]; 37 | for (int i = cr * len; i <= r; i++) 38 | ans += a[i]; 39 | return ans; 40 | } 41 | }; -------------------------------------------------------------------------------- /Combinatorics/Lucas/lucas.cpp: -------------------------------------------------------------------------------- 1 | ll f[N], rf[N]; 2 | ll bin(ll a, ll n, ll p) 3 | { 4 | ll ans = 1; 5 | while (n) 6 | { 7 | if (n & 1) 8 | ans *= a, ans %= p; 9 | a *= a, a %= p; 10 | n /= 2; 11 | } 12 | return ans; 13 | } 14 | void init(ll p) 15 | { 16 | f[0] = 1; 17 | for (ll i = 1; i < p; i++) 18 | f[i] = f[i - 1] * i % p; 19 | rf[p - 1] = bin(f[p - 1], p - 2, p); 20 | for (ll i = p - 2; i >= 0; i--) 21 | rf[i] = rf[i + 1] * (i + 1) % p; 22 | } 23 | ll c(ll n, ll k, ll p) 24 | { 25 | if (k < 0 || k > n) 26 | return 0; 27 | return f[n] * rf[k] % p * rf[n - k] % p; 28 | } 29 | ll lucas(ll n, ll k, ll p) 30 | { 31 | // https://github.com/bekzhan29/algos 32 | if (k < 0 || k > n) 33 | return 0; 34 | ll ans = 1; 35 | while (n) 36 | { 37 | ans *= c(n % p, k % p, p); 38 | ans %= p; 39 | n /= p; 40 | k /= p; 41 | } 42 | return ans; 43 | } -------------------------------------------------------------------------------- /Data Structures/Segment Tree/segment_tree3.cpp: -------------------------------------------------------------------------------- 1 | template 2 | struct segment_tree 3 | { 4 | // https://github.com/bekzhan29/algos 5 | private: 6 | int n1; 7 | vector tree; 8 | 9 | public: 10 | void build(int n, Type *a) 11 | { 12 | tree.resize(n + n1 + 2); 13 | n1 = 1; 14 | while (n1 < n) 15 | n1 *= 2; 16 | n1--; 17 | for (int i = 1; i <= n; i++) 18 | tree[i + n1] = a[i]; 19 | for (int i = (n1 + n) / 2; i >= 1; i--) 20 | tree[i] = tree[i * 2] + tree[i * 2 + 1]; 21 | } 22 | void upd(int pos, Type k) 23 | { 24 | pos += n1; 25 | tree[pos] += k; 26 | pos /= 2; 27 | for (; pos; pos /= 2) 28 | tree[pos] = tree[pos * 2] + tree[pos * 2 + 1]; 29 | } 30 | Type sum(int l, int r) 31 | { 32 | l += n1; 33 | r += n1; 34 | Type ans = 0; 35 | while (l <= r) 36 | { 37 | if (l & 1) 38 | ans += tree[l], l++; 39 | if (!(r & 1)) 40 | ans += tree[r], r--; 41 | l /= 2; 42 | r /= 2; 43 | } 44 | return ans; 45 | } 46 | }; -------------------------------------------------------------------------------- /Geometry/Convex Hull/graham.cpp: -------------------------------------------------------------------------------- 1 | struct point 2 | { 3 | ll x, y; 4 | bool operator==(const point &a) 5 | { 6 | return x == a.x && y == a.y; 7 | } 8 | bool operator<(const point &a) 9 | { 10 | if (x != a.x) 11 | return x < a.x; 12 | return y < a.y; 13 | } 14 | } a[N]; 15 | ll cross(point o, point a, point b) 16 | { 17 | return (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x); 18 | } 19 | vector convex_hull(ll n, point a[]) 20 | { 21 | // https://github.com/bekzhan29/algos 22 | vector ans; 23 | sort(a + 1, a + n + 1); 24 | ll sz = 0; 25 | for (ll i = 1; i <= n; i++) 26 | { 27 | while (sz >= 2 && cross(ans[sz - 1], ans[sz - 2], a[i]) >= 0) 28 | ans.pop_back(), sz--; 29 | ans.pb(a[i]); 30 | sz++; 31 | } 32 | ll t = sz; 33 | for (ll i = n - 1; i >= 1; i--) 34 | { 35 | while (sz > t && cross(ans[sz - 1], ans[sz - 2], a[i]) >= 0) 36 | ans.pop_back(), sz--; 37 | ans.pb(a[i]); 38 | sz++; 39 | } 40 | ans.pop_back(); 41 | return ans; 42 | } -------------------------------------------------------------------------------- /Data Structures/Cartesian Tree/cartesian_tree2.cpp: -------------------------------------------------------------------------------- 1 | struct item 2 | { 3 | // https://github.com/bekzhan29/algos 4 | ll x, y, val; 5 | item *l, *r; 6 | item() {} 7 | item(ll x, ll y, item *l = NULL, item *r = NULL) : x(x), y(y), l(l), r(r) {} 8 | }; 9 | typedef item *pitem; 10 | void upd(pitem &t) 11 | { 12 | if (!t) 13 | return; 14 | t->x = 1; 15 | if (t->l) 16 | t->x += t->l->x; 17 | if (t->r) 18 | t->x += t->r->x; 19 | } 20 | void merge(pitem &t, pitem l, pitem r) 21 | { 22 | if (!l || !r) 23 | { 24 | t = l ? l : r; 25 | return; 26 | } 27 | if (l->y < r->y) 28 | merge(l->r, l->r, r), t = l; 29 | else 30 | merge(r->l, l, r->l), t = r; 31 | upd(t); 32 | } 33 | void split(pitem t, ll k, pitem &l, pitem &r) 34 | { 35 | if (!t) 36 | { 37 | l = r = NULL; 38 | return; 39 | } 40 | ll cnt = 1; 41 | if (t->l) 42 | cnt += t->l->x; 43 | if (cnt <= k) 44 | split(t->r, k - cnt, t->r, r), l = t; 45 | else 46 | split(t->l, k, l, t->l), r = t; 47 | upd(l); 48 | upd(r); 49 | } -------------------------------------------------------------------------------- /Data Structures/Cartesian Tree/cartesian_tree1.cpp: -------------------------------------------------------------------------------- 1 | struct item 2 | { 3 | // https://github.com/bekzhan29/algos 4 | ll x, y; 5 | item *l, *r; 6 | item() {} 7 | item(ll x, ll y, item *l = NULL, item *r = NULL) : x(x), y(y), l(l), r(r) {} 8 | }; 9 | typedef item *pitem; 10 | void merge(pitem &t, pitem l, pitem r) 11 | { 12 | if (!l || !r) 13 | { 14 | t = l ? l : r; 15 | return; 16 | } 17 | if (l->y < r->y) 18 | merge(l->r, l->r, r), t = l; 19 | else 20 | merge(r->l, l, r->l), t = r; 21 | } 22 | void split(pitem t, ll k, pitem &l, pitem &r) 23 | { 24 | if (!t) 25 | { 26 | l = r = NULL; 27 | return; 28 | } 29 | if (t->x <= k) 30 | split(t->r, k, t->r, r), l = t; 31 | else 32 | split(t->l, k, l, t->l), r = t; 33 | } 34 | void add(pitem &t, pitem it) 35 | { 36 | if (!t) 37 | { 38 | t = it; 39 | return; 40 | } 41 | if (it->y < t->y) 42 | { 43 | split(t, it->x, it->l, it->r); 44 | t = it; 45 | return; 46 | } 47 | if (t->x < it->x) 48 | add(t->r, it); 49 | else 50 | add(t->l, it); 51 | } -------------------------------------------------------------------------------- /Data Structures/Fenwick Tree/fenwick_tree2.cpp: -------------------------------------------------------------------------------- 1 | template 2 | struct fenwick 3 | { 4 | private: 5 | int n; 6 | vector tree[2]; 7 | void add2(int ty, int pos, Type x) 8 | { 9 | // https://github.com/bekzhan29/algos 10 | for (; pos <= n; pos = (pos | (pos + 1))) 11 | tree[ty][pos] += x; 12 | } 13 | Type sum2(int ty, int pos) 14 | { 15 | ll ans = 0; 16 | for (; pos >= 0; pos = (pos & (pos + 1)) - 1) 17 | ans += tree[ty][pos]; 18 | return ans; 19 | } 20 | 21 | public: 22 | void add(int l, int r, Type x) 23 | { 24 | add2(0, l, x); 25 | add2(0, r + 1, -x); 26 | add2(1, l, (1 - l) * x); 27 | add2(1, r + 1, r * x); 28 | } 29 | Type sum(int l, int r) 30 | { 31 | return sum2(0, r) * r - sum2(0, l - 1) * (l - 1) + sum2(1, r) - sum2(1, l - 1); 32 | } 33 | void build(int n) 34 | { 35 | this->n = n; 36 | tree[0].resize(n + 1); 37 | tree[1].resize(n + 1); 38 | for (int i = 0; i <= n; i++) 39 | tree[0][i] = tree[1][i] = 0; 40 | for (int i = 1; i <= n; i++) 41 | add2(1, i, a[i]); 42 | } 43 | } -------------------------------------------------------------------------------- /Data Structures/Heap/heap.cpp: -------------------------------------------------------------------------------- 1 | template 2 | struct heap 3 | { 4 | // https://github.com/bekzhan29/algos 5 | private: 6 | int n; 7 | vector a; 8 | 9 | public: 10 | heap() 11 | { 12 | n = 0; 13 | a.resize(1); 14 | } 15 | void push(Type x) 16 | { 17 | int pos = ++n; 18 | a.push_back(x); 19 | while (pos > 1) 20 | { 21 | if (a[pos] >= a[pos / 2]) 22 | return; 23 | swap(a[pos], a[pos / 2]); 24 | pos /= 2; 25 | } 26 | } 27 | int size() 28 | { 29 | return n; 30 | } 31 | bool empty() 32 | { 33 | return n==0; 34 | } 35 | Type top() 36 | { 37 | return a[1]; 38 | } 39 | Type pop() 40 | { 41 | assert(n > 0); 42 | Type ans = top(); 43 | swap(a[1], a[n]); 44 | n--; 45 | a.pop_back(); 46 | int pos = 1; 47 | while (true) 48 | { 49 | int to = pos; 50 | if (pos * 2 <= n && a[pos] > a[pos * 2]) 51 | to = pos * 2; 52 | if (pos * 2 + 1 <= n && a[to] > a[pos * 2 + 1]) 53 | to = pos * 2 + 1; 54 | if (pos == to) 55 | break; 56 | swap(a[pos], a[to]); 57 | pos = to; 58 | } 59 | return ans; 60 | } 61 | }; -------------------------------------------------------------------------------- /Combinatorics/Partitions Of N/partitions_of_n.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | using namespace std; 13 | #define pb push_back 14 | #define INF 2e14 15 | #define mod 998244353 16 | #define eps 1e-9 17 | #define abs(x) ((x) >= 0 ? (x) : -(x)) 18 | #define y1 solai 19 | #define fi first 20 | #define se second 21 | typedef long long ll; 22 | typedef long double ld; 23 | typedef pair pll; 24 | const ll N = 100100; 25 | ll n, d[N]; 26 | int main() 27 | { 28 | // https://github.com/bekzhan29/algos 29 | ios_base::sync_with_stdio(0); 30 | cin.tie(0); 31 | cout.tie(0); 32 | 33 | cin >> n; 34 | d[0] = 1; 35 | for (ll i = 1; i <= n; i++) 36 | for (ll j = 1; (3 * j * j - j) / 2 <= i; j++) 37 | { 38 | d[i] += (j % 2 ? 1 : -1) * d[i - (3 * j * j - j) / 2]; 39 | if ((3 * j * j + j) / 2 <= i) 40 | d[i] += (j % 2 ? 1 : -1) * d[i - (3 * j * j + j) / 2]; 41 | d[i] %= mod; 42 | } 43 | cout << d[n]; 44 | } -------------------------------------------------------------------------------- /Dynamic Programming/Li Chao Tree/li_chao_tree.cpp: -------------------------------------------------------------------------------- 1 | struct line 2 | { 3 | // https://github.com/bekzhan29/algos 4 | ll k, b; 5 | } t[4 * N]; 6 | void build(ll v, ll l, ll r) 7 | { 8 | if (l == r) 9 | { 10 | t[v] = {INF, INF}; 11 | return; 12 | } 13 | ll mid = (l + r) / 2; 14 | build(v * 2, l, mid); 15 | build(v * 2 + 1, mid + 1, r); 16 | t[v] = {INF, INF}; 17 | } 18 | ll get(line l, ll x) 19 | { 20 | return l.k * x + l.b; 21 | } 22 | void upd(ll v, ll l, ll r, line x) 23 | { 24 | if (l == r) 25 | { 26 | if (get(x, l) <= get(t[v], l)) 27 | t[v] = x; 28 | return; 29 | } 30 | ll mid = (l + r) / 2; 31 | ll left = get(x, l) <= get(t[v], l); 32 | ll middle = get(x, mid) <= get(t[v], mid); 33 | if (middle == 1) 34 | swap(t[v], x); 35 | if (left != middle) 36 | upd(v * 2, l, mid, x); 37 | else 38 | upd(v * 2 + 1, mid + 1, r, x); 39 | } 40 | line minn(ll v, ll l, ll r, ll x) 41 | { 42 | if (l == r) 43 | return t[v], x; 44 | ll mid = (l + r) / 2; 45 | line y; 46 | if (x <= mid) 47 | y = minn(v * 2, l, mid, x); 48 | else 49 | y = minn(v * 2 + 1, mid + 1, r, x); 50 | if (get(t[v], x) <= get(y, x)) 51 | return t[v]; 52 | return y; 53 | } -------------------------------------------------------------------------------- /Combinatorics/Binary Pow Matrices/binary_pow_matrices.cpp: -------------------------------------------------------------------------------- 1 | struct matrix 2 | { 3 | // https://github.com/bekzhan29/algos 4 | ll n, m, p; 5 | vector> a; 6 | void resize(ll n, ll m, ll p) 7 | { 8 | this->n = n; 9 | this->m = m; 10 | this->p = p; 11 | a.resize(n); 12 | for (ll i = 0; i < n; i++) 13 | { 14 | a[i].resize(m); 15 | for (ll j = 0; j < m; j++) 16 | a[i][j] = 0; 17 | } 18 | } 19 | matrix(int n = 1, int m = 1, int p = 2) 20 | { 21 | resize(n, m, p); 22 | } 23 | matrix &operator*=(const matrix &b) 24 | { 25 | assert(m == b.n); 26 | matrix c(n, b.m, p); 27 | for (ll i = 0; i < n; i++) 28 | for (ll j = 0; j < b.m; j++) 29 | for (ll k = 0; k < m; k++) 30 | { 31 | c.a[i][j] += 1LL * a[i][k] * b.a[k][j] % p; 32 | if (c.a[i][j] >= p) 33 | c.a[i][j] -= p; 34 | } 35 | n = c.n; 36 | m = c.m; 37 | a = c.a; 38 | return *this; 39 | } 40 | }; 41 | matrix bin(matrix a, ll k) 42 | { 43 | assert(a.n == a.m); 44 | matrix ans(a.n, a.n, a.p); 45 | for (ll i = 0; i < ans.n; i++) 46 | ans.a[i][i] = 1; 47 | while (k) 48 | { 49 | if (k & 1) 50 | ans *= a; 51 | a *= a; 52 | k /= 2; 53 | } 54 | return ans; 55 | } -------------------------------------------------------------------------------- /Graph Theory/Least Common Ancestor/lca2.cpp: -------------------------------------------------------------------------------- 1 | ll sz, pos[N], ord[2 * N], dep[2 * N]; 2 | pll tree[8 * N]; 3 | vector v[N]; 4 | void dfs(ll x, ll par, ll depth) 5 | { 6 | // https://github.com/bekzhan29/algos 7 | ord[++sz] = x; 8 | dep[sz] = depth; 9 | pos[x] = sz; 10 | for (ll to : v[x]) 11 | if (to != par) 12 | { 13 | dfs(to, x, depth + 1); 14 | ord[++sz] = x; 15 | dep[sz] = depth; 16 | } 17 | } 18 | void build(ll v, ll l, ll r) 19 | { 20 | if (l == r) 21 | { 22 | tree[v] = {dep[l], l}; 23 | return; 24 | } 25 | ll mid = (l + r) / 2; 26 | build(v * 2, l, mid); 27 | build(v * 2 + 1, mid + 1, r); 28 | tree[v] = min(tree[v * 2], tree[v * 2 + 1]); 29 | } 30 | void init(ll root) 31 | { 32 | sz = 0; 33 | dfs(root, 0, 1); 34 | build(1, 1, sz); 35 | } 36 | pll minn(ll v, ll l, ll r, ll x, ll y) 37 | { 38 | if (x > y || x > r || y < l) 39 | return {INF, 0}; 40 | if (x <= l && r <= y) 41 | return tree[v]; 42 | ll mid = (l + r) / 2; 43 | return min(minn(v * 2, l, mid, x, y), minn(v * 2 + 1, mid + 1, r, x, y)); 44 | } 45 | ll lca(ll x, ll y) 46 | { 47 | if (pos[x] > pos[y]) 48 | swap(x, y); 49 | pll ans = minn(1, 1, sz, pos[x], pos[y]); 50 | return ord[ans.se]; 51 | } -------------------------------------------------------------------------------- /Graph Theory/Ford Fulkerson/ford_fulkerson.cpp: -------------------------------------------------------------------------------- 1 | struct max_flow 2 | { 3 | // https://github.com/bekzhan29/algos 4 | int n; 5 | vector> f, c; 6 | vector w; 7 | max_flow(int n) 8 | { 9 | this->n = n; 10 | f.resize(n + 1); 11 | c.resize(n + 1); 12 | w.resize(n + 1); 13 | for (int i = 1; i <= n; i++) 14 | { 15 | f[i].resize(n + 1); 16 | c[i].resize(n + 1); 17 | for (int j = 1; j <= n; j++) 18 | f[i][j] = c[i][j] = 0; 19 | } 20 | } 21 | int dfs(int x, int t, int flow) 22 | { 23 | if (x == t) 24 | return flow; 25 | w[x] = 1; 26 | for (int to = 1; to <= n; to++) 27 | if (!w[to] && c[x][to] - f[x][to] > 0) 28 | { 29 | int delta = dfs(to, t, min(flow, c[x][to] - f[x][to])); 30 | f[x][to] += delta; 31 | f[to][x] -= delta; 32 | if (delta) 33 | return delta; 34 | } 35 | return 0; 36 | } 37 | int flow(int s, int t) 38 | { 39 | int ans = 0; 40 | for (int i = 1; i <= n; i++) 41 | for (int j = 1; j <= n; j++) 42 | f[i][j] = 0; 43 | for (;;) 44 | { 45 | for (int i = 1; i <= n; i++) 46 | w[i] = 0; 47 | int flow = dfs(s, t, INF); 48 | ans += flow; 49 | if (!flow) 50 | break; 51 | } 52 | return ans; 53 | } 54 | }; -------------------------------------------------------------------------------- /Editorials/IZhO/2017/Money/readme.md: -------------------------------------------------------------------------------- 1 | # Банкноты 2 | Заметим что числа в каждом из отрезков должны неубывать, иначе финальный массив будет неотсортирован. Еще заметим что числа на позициях $i$ и $j$ такие что $ia[i]$, а числа в отрезке должны неубывать. Значит числа с позиции $i$ до $j$ будут либо все слева от $k$, либо справа. Если поставить отрезок $i$, $j$ слева от $k$, то массив будет неотсортирован так как $a[j]>a[k]$. Аналогично если поставить отрезок $i$, $j$ справа от $k$, то массив будет неотсортирован потому что $a[k]>a[i]$. 3 | 4 | Начнем с первого позиции, будем брать числа пока отрезок неубывает. Для последующих отрезков надо брать числа пока они неубывают и ни одно из чисел из прошлых отрезков не находится между $a[l]$ и $a[r]$, где $l$ это начало текущего отрезка и $r$ его конец. Можно добавить все числа из предыдущих отрезков в сет в `C++` и найти минимальное число которое больше чем $a[l]$, это можно сделать с помощью `set.upper_bound()`. Назовем это число $x$. Будем брать числа в отрезок до тех пор пока они неубывают и не больше чем $x$. 5 | 6 | Время работы $O(NlogN)$. -------------------------------------------------------------------------------- /Graph Theory/Boruvka/boruvka.cpp: -------------------------------------------------------------------------------- 1 | ll n, m, p[N], r[N], msz; 2 | pair minn[N]; 3 | struct edge 4 | { 5 | ll x, y, w; 6 | edge(ll x = 0, ll y = 0, ll w = 0) : x(x), y(y), w(w) {} 7 | } e[N], mst[N]; 8 | void init_dsu(ll n) 9 | { 10 | for (ll i = 1; i <= n; i++) 11 | p[i] = i, r[i] = 1; 12 | } 13 | ll fin(ll a) 14 | { 15 | if (a == p[a]) 16 | return a; 17 | return p[a] = fin(p[a]); 18 | } 19 | bool uni(ll a, ll b) 20 | { 21 | a = fin(a), b = fin(b); 22 | if (a == b) 23 | return 0; 24 | if (r[a] > r[b]) 25 | r[a] += r[b], p[b] = a; 26 | else 27 | r[b] += r[a], p[a] = b; 28 | return 1; 29 | } 30 | void boruvka(ll n, ll m) 31 | { 32 | // https://github.com/bekzhan29/algos 33 | init_dsu(n); 34 | msz = 0; 35 | ll n_cmp = n; 36 | for (; n_cmp > 1;) 37 | { 38 | for (ll i = 1; i <= n; i++) 39 | minn[i] = {INF, 0}; 40 | for (ll i = 1; i <= m; i++) 41 | { 42 | ll x = fin(e[i].x), y = fin(e[i].y), weight = e[i].w; 43 | if (x == y) 44 | continue; 45 | minn[x] = min(minn[x], {weight, i}); 46 | minn[y] = min(minn[y], {weight, i}); 47 | } 48 | for (ll i = 1; i <= n; i++) 49 | { 50 | if (i != fin(i)) 51 | continue; 52 | ll pos = minn[i].se; 53 | if (uni(e[pos].x, e[pos].y)) 54 | { 55 | mst[++msz] = e[pos]; 56 | n_cmp--; 57 | } 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /Data Structures/Persistent Segment Tree/persistent_segment_tree.cpp: -------------------------------------------------------------------------------- 1 | ll a[N], root[N], sz; 2 | struct node 3 | { 4 | // https://github.com/bekzhan29/algos 5 | ll sum, l, r; 6 | node() : sum(0), l(0), r(0) {} 7 | } tree[N]; 8 | void build(ll &v, ll l, ll r) 9 | { 10 | if (!v) 11 | v = ++sz; 12 | if (l == r) 13 | { 14 | tree[v].sum = a[l]; 15 | return; 16 | } 17 | ll mid = (l + r) / 2; 18 | build(tree[v].l, l, mid); 19 | build(tree[v].r, mid + 1, r); 20 | ll left = tree[v].l, right = tree[v].r; 21 | tree[v].sum = tree[left].sum + tree[right].sum; 22 | } 23 | void upd(ll v1, ll &v2, ll l, ll r, ll pos, ll k) 24 | { 25 | if (!v2) 26 | v2 = ++sz; 27 | if (l == r) 28 | { 29 | tree[v2].sum = tree[v1].sum + k; 30 | return; 31 | } 32 | ll mid = (l + r) / 2; 33 | if (pos <= mid) 34 | tree[v2].r = tree[v1].r, 35 | upd(tree[v1].l, tree[v2].l, l, mid, pos, k); 36 | else 37 | tree[v2].l = tree[v1].l, 38 | upd(tree[v1].r, tree[v2].r, mid + 1, r, pos, k); 39 | ll left = tree[v2].l, right = tree[v2].r; 40 | tree[v2].sum = tree[left].sum + tree[right].sum; 41 | } 42 | ll sum(ll v, ll l, ll r, ll x, ll y) 43 | { 44 | if (x > y || x > r || y < l) 45 | return 0; 46 | if (x <= l && r <= y) 47 | return tree[v].sum; 48 | ll mid = (l + r) / 2; 49 | return sum(tree[v].l, l, mid, x, y) + sum(tree[v].r, mid + 1, r, x, y); 50 | } -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Библиотека алгоритмов 2 | Главная цель этой библиотеки в том чтобы легко быть использованной людьми. Можно запросто скопировать любой алгоритм в свой код и воспользоваться им не читая его описание. 3 | 4 | Любой желающий может использовать их. А так же пополнять ее своими алгоритмами просто отправив [pull request](https://docs.github.com/ru/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request). Или можно написать мне в [Телеграм](https://t.me/bekzhan29). 5 | # Как помочь проекту? 6 | Можете дополнять библиотеку алгоритмами которых здесь нет или добавлять описание к уже существующим алгоритмам. Также можно добавлять ссылки на статьи и видео. 7 | # Algorithms library 8 | Main goal of this library is to be easy to use. You can easily copy any algorithm in your code and use it without reading its description. 9 | 10 | Anyone can use these algorithms. And add there own algos here through [pull request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request). Or you can message me in [Telegram](https://t.me/bekzhan29). 11 | # How to help this project? 12 | You can add algorithms that are not present in this library or you can add description of existing algorithms. You can also add links to posts or videos. -------------------------------------------------------------------------------- /Other/Annealing Optimization/annealing.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | typedef long long ll; 4 | ll n, a[111111], b[111111], ans, calc, l[222222], r[222222], x, y; 5 | double t; 6 | ll f() 7 | { 8 | ll ans = 0; 9 | for (ll i = 1; i <= n; i++) 10 | ans += r[i + b[i]]++, 11 | ans += l[i - b[i] + n]++; 12 | return ans; 13 | } 14 | int main() 15 | { 16 | cin >> n; 17 | for (ll i = 1; i <= n; i++) 18 | a[i] = b[i] = i; 19 | ans = f(); 20 | srand(time(0)); 21 | t = 10; 22 | for (; ans > 0;) 23 | { 24 | x = rand() % n + 1, y = rand() % n + 1; 25 | swap(b[x], b[y]); 26 | calc = ans; 27 | calc -= --r[x + a[x]]; 28 | calc -= --l[x - a[x] + n]; 29 | calc -= --r[y + a[y]]; 30 | calc -= --l[y - a[y] + n]; 31 | calc += r[x + b[x]]++; 32 | calc += l[x - b[x] + n]++; 33 | calc += r[y + b[y]]++; 34 | calc += l[y - b[y] + n]++; 35 | if (ans > calc || exp((ans - calc) / t) >= (rand() % 1000001) / 1000000.0) 36 | { 37 | ans = calc; 38 | swap(a[x], a[y]); 39 | } 40 | else 41 | { 42 | --r[x + b[x]]; 43 | --l[x - b[x] + n]; 44 | --r[y + b[y]]; 45 | --l[y - b[y] + n]; 46 | ++r[x + a[x]]; 47 | ++l[x - a[x] + n]; 48 | ++r[y + a[y]]; 49 | ++l[y - a[y] + n]; 50 | swap(b[x], b[y]); 51 | } 52 | t *= 0.99; 53 | } 54 | for (ll i = 1; i <= n; i++) 55 | r[i + b[i]] = l[i - b[i] + n] = 0; 56 | cout << f() << endl; 57 | } -------------------------------------------------------------------------------- /Data Structures/Segment Tree/readme.md: -------------------------------------------------------------------------------- 1 | Дерево отрезков позволяет выполнять следующие запросы 2 | 1. Изменить $a[i]$ += $x$. $O(logN)$ 3 | 2. Изменить $a[i]$ += $x$ для всех $i$ на отрезке от $l$ до $r$. $O(logN)$ 4 | 3. Найти сумму на отрезке от $l$ до $r$. $O(logN)$ 5 | 6 | Построение дерева отрезков занимает $O(N)$ времени 7 | 8 |
9 | Пример использования segment_tree1.cpp 10 | 11 | ```cpp 12 | segment_tree t; 13 | // Построить дерево отрезков по массиву a длины n 14 | t.build(n, a); 15 | // Добавить k к элементу на позиции x 16 | t.upd(x, k); 17 | // Вывести сумму на отрезке от l до r 18 | cout << t.sum(l, r); 19 | ``` 20 |
21 | 22 |
23 | Пример использования segment_tree2.cpp 24 | 25 | ```cpp 26 | segment_tree t; 27 | // Построить дерево отрезков по массиву a длины n 28 | t.build(n, a); 29 | // Добавить k ко всем элементам от l до r 30 | t.upd(l, r, k); 31 | // Вывести сумму на отрезке от l до r 32 | cout << t.sum(l, r); 33 | ``` 34 |
35 | 36 |
37 | Пример использования segment_tree3.cpp 38 | 39 | ```cpp 40 | segment_tree t; 41 | // Построить дерево отрезков по массиву a длины n 42 | t.build(n, a); 43 | // Добавить k к элементу на позиции x 44 | t.upd(x, k); 45 | // Вывести сумму на отрезке от l до r 46 | cout << t.sum(l, r); 47 | ``` 48 |
49 | -------------------------------------------------------------------------------- /Combinatorics/Fast Fourier Transform/fast_fourier_transform.cpp: -------------------------------------------------------------------------------- 1 | double pi = acos(-1); 2 | struct complexx 3 | { 4 | // https://github.com/bekzhan29/algos 5 | double x, y; 6 | complexx(double x = 0, double y = 0) : x(x), y(y) {} 7 | complexx operator*(const complexx &a) 8 | { 9 | complexx c; 10 | c.x = x * a.x - y * a.y; 11 | c.y = x * a.y + y * a.x; 12 | return c; 13 | } 14 | } a[N], b[N], c[N]; 15 | ll rev(ll x, ll n) 16 | { 17 | ll ans = 0; 18 | for (ll i = 1; i <= n; i++) 19 | { 20 | ans = ans * 2 + x % 2; 21 | x /= 2; 22 | } 23 | return ans; 24 | } 25 | void fft(ll n, complexx *a, bool ch) 26 | { 27 | ll log = 0; 28 | while ((1 << log) < n) 29 | log++; 30 | for (ll i = 0; i < n; i++) 31 | { 32 | ll j = rev(i, log); 33 | if (i < j) 34 | swap(a[i], a[j]); 35 | } 36 | for (ll len = 2; len <= n; len *= 2) 37 | { 38 | ll len2 = len / 2; 39 | complexx w, w1; 40 | double ang = 2 * pi / len * (ch ? -1 : 1); 41 | w1 = complexx(cos(ang), sin(ang)); 42 | for (ll i = 0; i < n; i += len) 43 | { 44 | w.x = 1; 45 | w.y = 0; 46 | for (ll j = 0; j < len2; j++) 47 | { 48 | complexx u = a[i + j], v = a[i + j + len2] * w; 49 | a[i + j].x = u.x + v.x; 50 | a[i + j].y = u.y + v.y; 51 | a[i + j + len2].x = u.x - v.x; 52 | a[i + j + len2].y = u.y - v.y; 53 | w = w * w1; 54 | } 55 | } 56 | } 57 | if (ch) 58 | for (ll i = 0; i < n; i++) 59 | a[i].x /= n, a[i].y /= n; 60 | } -------------------------------------------------------------------------------- /Data Structures/Mo/mo.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | #define fi first 4 | #define se second 5 | typedef long long ll; 6 | typedef pair pll; 7 | const ll N = 200400; 8 | ll n, m, a[N], l, r, k, cnt[N], ans[N], res, tl, tr, pos; 9 | vector> v[N]; 10 | void mo() 11 | { 12 | // https://github.com/bekzhan29/algos 13 | for (ll i = 0; i <= n / k; i++) 14 | { 15 | for (ll j = 0; j < N; j++) 16 | cnt[j] = 0; 17 | res = 0; 18 | l = 1, r = 0; 19 | sort(v[i].begin(), v[i].end()); 20 | for (ll j = 0; j < v[i].size(); j++) 21 | { 22 | tl = v[i][j].fi.se; 23 | tr = v[i][j].fi.fi; 24 | pos = v[i][j].se; 25 | while (r < tr) 26 | { 27 | cnt[a[++r]]++; 28 | if (cnt[a[r]] >= 3) 29 | res += (cnt[a[r]] - 1) * (cnt[a[r]] - 2) / 2; 30 | } 31 | while (l < tl) 32 | { 33 | ll x = a[l++]; 34 | cnt[x]--; 35 | if (cnt[x] >= 2) 36 | res -= cnt[x] * (cnt[x] - 1) / 2; 37 | } 38 | while (l > tl) 39 | { 40 | ll x = a[--l]; 41 | cnt[x]++; 42 | if (cnt[x] >= 3) 43 | res += (cnt[x] - 1) * (cnt[x] - 2) / 2; 44 | } 45 | ans[pos] = res; 46 | } 47 | } 48 | } 49 | int main() 50 | { 51 | cin >> n >> m; 52 | k = sqrt(n); 53 | for (ll i = 1; i <= n; i++) 54 | cin >> a[i]; 55 | for (ll i = 1; i <= m; i++) 56 | cin >> l >> r, v[l / k].push_back({{r, l}, i}); 57 | mo(); 58 | for (ll i = 1; i <= m; i++) 59 | cout << ans[i] << "\n"; 60 | } -------------------------------------------------------------------------------- /Editorials/IZhO/2017/Money/english.md: -------------------------------------------------------------------------------- 1 | # Money 2 | Note that the numbers in each segment must be non-decreasing, otherwise the final array will be unsorted. Also note that the numbers at positions $i$ and $j$ such that $ia[i]$, and the numbers in the segment must be non-decreasing. This means that the numbers from position $i$ to $j$ will either be all to the left of $k$, or all to the right. If you put the segment $i$, $j$ to the left of $k$, the array will be unsorted since $a[j]>a[k]$. Similarly, if you put the segment $i$, $j$ to the right of $k$, the array will be unsorted because $a[k]>a[i]$. 3 | 4 | Let's start from the first position, we will take numbers while the segment is non-decreasing. For subsequent segments, we need to take numbers while they are non-decreasing and none of the numbers from the previous segments is between $a[l]$ and $a[r]$, where $l$ is the beginning of the current segment and $r$ is its end. You can add all the numbers from the previous segments to a set in `C++` and find the minimum number that is greater than $a[l]$, this can be done using `set.upper_bound()`. Let's call this number $x$. We will take numbers into the segment until they are non-decreasing and not greater than $x$. 5 | 6 | The running time is $O(NlogN)$. -------------------------------------------------------------------------------- /Data Structures/Segment Tree/segment_tree1.cpp: -------------------------------------------------------------------------------- 1 | template 2 | struct segment_tree 3 | { 4 | // https://github.com/bekzhan29/algos 5 | private: 6 | int n; 7 | vector tree; 8 | void build_tree(int v, int vl, int vr, Type *a) 9 | { 10 | if (vl == vr) 11 | { 12 | if (a == NULL) 13 | tree[v] = 0; 14 | else 15 | tree[v] = a[vl]; 16 | return; 17 | } 18 | int mid = (vl + vr) / 2; 19 | build_tree(v * 2, vl, mid, a); 20 | build_tree(v * 2 + 1, mid + 1, vr, a); 21 | tree[v] = tree[v * 2] + tree[v * 2 + 1]; 22 | } 23 | void upd_tree(int v, int vl, int vr, int pos, Type k) 24 | { 25 | if (vl == vr) 26 | { 27 | tree[v] += k; 28 | return; 29 | } 30 | int mid = (vl + vr) / 2; 31 | if (pos <= mid) 32 | upd_tree(v * 2, vl, mid, pos, k); 33 | else 34 | upd_tree(v * 2 + 1, mid + 1, vr, pos, k); 35 | tree[v] = tree[v * 2] + tree[v * 2 + 1]; 36 | } 37 | Type get_tree(int v, int vl, int vr, int l, int r) 38 | { 39 | if (l > r || l > vr || r < vl) 40 | return 0; 41 | if (l <= vl && vr <= r) 42 | return tree[v]; 43 | int mid = (vl + vr) / 2; 44 | return get_tree(v * 2, vl, mid, l, r) + get_tree(v * 2 + 1, mid + 1, vr, l, r); 45 | } 46 | 47 | public: 48 | void build(int len, Type *a=NULL) 49 | { 50 | n = len; 51 | tree.resize(4 * n); 52 | build_tree(1, 1, n, a); 53 | } 54 | void upd(int pos, Type k) 55 | { 56 | upd_tree(1, 1, n, pos, k); 57 | } 58 | Type get(int l, int r) 59 | { 60 | return get_tree(1, 1, n, l, r); 61 | } 62 | }; -------------------------------------------------------------------------------- /Graph Theory/Edmonds Karp/edmonds_karp.cpp: -------------------------------------------------------------------------------- 1 | struct max_flow 2 | { 3 | // https://github.com/bekzhan29/algos 4 | int n; 5 | vector> f, c; 6 | vector w, d, p, g; 7 | max_flow(int n) 8 | { 9 | this->n = n; 10 | f.resize(n + 1); 11 | c.resize(n + 1); 12 | w.resize(n + 1); 13 | d.resize(n + 1); 14 | p.resize(n + 1); 15 | g.resize(n + 1); 16 | for (int i = 1; i <= n; i++) 17 | { 18 | f[i].resize(n + 1); 19 | c[i].resize(n + 1); 20 | for (int j = 1; j <= n; j++) 21 | f[i][j] = c[i][j] = 0; 22 | } 23 | } 24 | int bfs(int s, int t) 25 | { 26 | for (int i = 1; i <= n; i++) 27 | d[i] = n + 1, p[i] = g[i] = 0; 28 | d[s] = 0; 29 | g[s] = INF; 30 | queue q; 31 | q.push(s); 32 | for (; !q.empty();) 33 | { 34 | int x = q.front(); 35 | q.pop(); 36 | for (int to = 1; to <= n; to++) 37 | if (d[to] > d[x] + 1 && c[x][to] > f[x][to]) 38 | { 39 | d[to] = d[x] + 1; 40 | p[to] = x; 41 | g[to] = min(g[x], c[x][to] - f[x][to]); 42 | q.push(to); 43 | } 44 | } 45 | int delta = g[t]; 46 | for (int x = t; p[x]; x = p[x]) 47 | { 48 | f[p[x]][x] += delta; 49 | f[x][p[x]] -= delta; 50 | } 51 | return delta; 52 | } 53 | int flow(int s, int t) 54 | { 55 | int ans = 0; 56 | for (int i = 1; i <= n; i++) 57 | for (int j = 1; j <= n; j++) 58 | f[i][j] = 0; 59 | for (;;) 60 | { 61 | for (int i = 1; i <= n; i++) 62 | w[i] = 0; 63 | int flow = bfs(s, t); 64 | ans += flow; 65 | if (!flow) 66 | break; 67 | } 68 | return ans; 69 | } 70 | }; -------------------------------------------------------------------------------- /Graph Theory/Disjoint Set Union/dsu_tree.cpp: -------------------------------------------------------------------------------- 1 | struct dsu_tree 2 | { 3 | int cnt, timer, lg, phase = 0; 4 | vector p, r, t, par; 5 | vector> pr; 6 | void init(int n) 7 | { 8 | assert(phase == 0); 9 | phase++; 10 | cnt = n; 11 | p.resize(2 * n); 12 | r.resize(2 * n); 13 | t.resize(2 * n); 14 | par.resize(2 * n); 15 | for (int i = 1; i < 2 * n; i++) 16 | p[i] = par[i] = i, r[i] = 1, t[i] = 0; 17 | } 18 | int fin(int a) 19 | { 20 | assert(phase == 1); 21 | if (a == p[a]) 22 | return a; 23 | return p[a] = fin(p[a]); 24 | } 25 | bool uni(int a, int b) 26 | { 27 | assert(phase == 1); 28 | a = fin(a), b = fin(b); 29 | timer++; 30 | if (a == b) 31 | return false; 32 | cnt++; 33 | par[a] = par[b] = p[a] = p[b] = cnt; 34 | r[cnt] = r[a] + r[b]; 35 | t[cnt] = timer; 36 | return true; 37 | } 38 | void init2() 39 | { 40 | assert(phase == 1); 41 | phase++; 42 | lg = 0; 43 | while ((1 << lg) <= cnt) 44 | lg++; 45 | pr.resize(lg); 46 | for (int i = 0; i < lg; i++) 47 | pr[i].resize(cnt + 1); 48 | for (int i = 0; i < lg; i++) 49 | pr[i][0] = 0; 50 | for (int i = 1; i <= cnt; i++) 51 | pr[0][i] = par[i]; 52 | for (int j = 1; j < lg; j++) 53 | for (int i = 1; i <= cnt; i++) 54 | pr[j][i] = pr[j - 1][pr[j - 1][i]]; 55 | } 56 | int fin(int ti, int a) 57 | { 58 | assert(phase == 2); 59 | for (int i = lg - 1; i >= 0; i--) 60 | if (t[pr[i][a]] <= ti) 61 | a = pr[i][a]; 62 | return a; 63 | } 64 | int sz(int a) 65 | { 66 | assert(phase == 2); 67 | return r[a]; 68 | } 69 | }; -------------------------------------------------------------------------------- /Combinatorics/Lagrange Interpolation/lagrange1.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | using namespace std; 15 | #define pb push_back 16 | #define mp make_pair 17 | #define INF ll(2e15) 18 | #define mod 998244353 19 | #define eps 1e-9 20 | #define abs(x) ((x) >= 0 ? (x) : -(x)) 21 | #define y1 solai 22 | #define fi first 23 | #define se second 24 | typedef long long ll; 25 | typedef long double ld; 26 | typedef pair pll; 27 | typedef pair pdd; 28 | const ll N = 5100; 29 | ll n, k, a[N], x, cur, ans; 30 | ll bin(ll a, ll n) 31 | { 32 | ll ans = 1; 33 | while (n) 34 | { 35 | if (n & 1) 36 | ans *= a, ans %= mod; 37 | a *= a, a %= mod; 38 | n /= 2; 39 | } 40 | return ans; 41 | } 42 | ll f(ll x) 43 | { 44 | // https://github.com/bekzhan29/algos 45 | ll ans = 0; 46 | for (ll i = 1; i <= x; i++) 47 | ans += bin(i, k), ans %= mod; 48 | return ans; 49 | } 50 | int main() 51 | { 52 | ios_base::sync_with_stdio(0); 53 | cin.tie(0); 54 | 55 | cin >> x >> k; 56 | n = k + 2; 57 | for (ll i = 1; i <= n; i++) 58 | a[i] = a[i - 1] + bin(i, k), a[i] %= mod; 59 | for (ll i = 1; i <= n; i++) 60 | { 61 | cur = a[i]; 62 | for (ll j = 1; j <= n; j++) 63 | if (i != j) 64 | cur = cur * (x - j + mod) % mod * bin((i - j + mod) % mod, mod - 2) % mod; 65 | ans += cur; 66 | ans %= mod; 67 | } 68 | cout << ans; 69 | } -------------------------------------------------------------------------------- /Data Structures/Sqrt Decomposition/sqrt_decomposition2.cpp: -------------------------------------------------------------------------------- 1 | template 2 | struct sqrt_dec 3 | { 4 | // https://github.com/bekzhan29/algos 5 | int len; 6 | vector a, bl, zl; 7 | void build(int n, Type *ar) 8 | { 9 | len = sqrt(n); 10 | bl.resize(n / len + 1); 11 | zl.resize(n / len + 1); 12 | a.push_back(0); 13 | for (int i = 1; i <= n; i++) 14 | { 15 | a.push_back(ar[i]); 16 | bl[i / len] += ar[i]; 17 | } 18 | } 19 | void push(int pos) 20 | { 21 | if (zl[pos] == 0) 22 | return; 23 | for (int i = pos * len; i < (pos + 1) * len; i++) 24 | a[i] += zl[pos]; 25 | zl[pos] = 0; 26 | } 27 | void upd(int l, int r, Type k) 28 | { 29 | int cl = l / len, cr = r / len; 30 | if (cl == cr) 31 | { 32 | push(cl); 33 | for (int i = l; i <= r; i++) 34 | a[i] += k, bl[cl] += k; 35 | return; 36 | } 37 | push(cl); 38 | push(cr); 39 | for (int i = l; i < (cl + 1) * len; i++) 40 | a[i] += k, bl[cl] += k; 41 | for (int i = cl + 1; i < cr; i++) 42 | zl[i] += k, bl[i] += len * k; 43 | for (int i = cr * len; i <= r; i++) 44 | a[i] += k, bl[cr] += k; 45 | } 46 | Type sum(int l, int r) 47 | { 48 | Type ans = 0; 49 | int cl = l / len, cr = r / len; 50 | if (cl == cr) 51 | { 52 | push(cl); 53 | for (int i = l; i <= r; i++) 54 | ans += a[i]; 55 | return ans; 56 | } 57 | push(cl); 58 | push(cr); 59 | for (int i = l; i < (cl + 1) * len; i++) 60 | ans += a[i]; 61 | for (int i = cl + 1; i < cr; i++) 62 | ans += bl[i]; 63 | for (int i = cr * len; i <= r; i++) 64 | ans += a[i]; 65 | return ans; 66 | } 67 | }; -------------------------------------------------------------------------------- /Combinatorics/Lagrange Interpolation/lagrange2.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | using namespace std; 15 | #define pb push_back 16 | #define mp make_pair 17 | #define INF ll(2e15) 18 | #define mod 1000000007 19 | #define eps 1e-9 20 | #define abs(x) ((x) >= 0 ? (x) : -(x)) 21 | #define y1 solai 22 | #define fi first 23 | #define se second 24 | typedef long long ll; 25 | typedef long double ld; 26 | typedef pair pll; 27 | typedef pair pdd; 28 | const ll N = 1000100; 29 | ll n, k, a[N], pr[N], su[N], x, cur, ans, calc; 30 | ll bin(ll a, ll n) 31 | { 32 | ll ans = 1; 33 | while (n) 34 | { 35 | if (n & 1) 36 | ans *= a, ans %= mod; 37 | a *= a, a %= mod; 38 | n /= 2; 39 | } 40 | return ans; 41 | } 42 | ll f(ll x) 43 | { 44 | // https://github.com/bekzhan29/algos 45 | ll ans = 0; 46 | for (ll i = 1; i <= x; i++) 47 | ans += bin(i, k), ans %= mod; 48 | return ans; 49 | } 50 | int main() 51 | { 52 | ios_base::sync_with_stdio(0); 53 | cin.tie(0); 54 | 55 | cin >> x >> k; 56 | n = k + 2; 57 | if (x <= n) 58 | return cout << f(x), 0; 59 | calc = 1; 60 | pr[0] = su[0] = 1; 61 | for (ll i = 1; i <= n; i++) 62 | { 63 | a[i] = a[i - 1] + bin(i, k), a[i] %= mod; 64 | pr[i] = pr[i - 1] * i, pr[i] %= mod; 65 | su[i] = su[i - 1] * (mod - i), su[i] %= mod; 66 | calc *= (x - i + mod) % mod; 67 | calc %= mod; 68 | } 69 | for (ll i = 1; i <= n; i++) 70 | { 71 | cur = a[i] * calc % mod * bin((x - i + mod) % mod, mod - 2) % mod * bin(pr[i - 1], mod - 2) % mod * bin(su[n - i], mod - 2) % mod; 72 | ans += cur; 73 | ans %= mod; 74 | } 75 | cout << ans; 76 | } -------------------------------------------------------------------------------- /Combinatorics/Generating Functions/generating_functions1.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | #define pb push_back 4 | #define mp make_pair 5 | #define INF ll(1e18) 6 | #define mod2 998244353 7 | #define mod 1000000007 8 | #define eps 1e-9 9 | #define abs(x) ((x) >= 0 ? (x) : -(x)) 10 | #define y1 solai 11 | #define fi first 12 | #define se second 13 | typedef long long ll; 14 | typedef long double ld; 15 | typedef pair pll; 16 | typedef pair pdd; 17 | typedef pair plll; 18 | mt19937 rng(chrono::steady_clock::now().time_since_epoch().count()); 19 | const ll N = 10100; 20 | ll n, k, a[N], x[N], f[N], rf[N], ans; 21 | ll bin(ll a, ll n) 22 | { 23 | ll ans = 1; 24 | while (n) 25 | { 26 | if (n & 1) 27 | ans *= a, ans %= mod; 28 | a *= a, a %= mod; 29 | n /= 2; 30 | } 31 | return ans; 32 | } 33 | ll c(ll n, ll k) 34 | { 35 | if (k < 0 || k > n) 36 | return 0; 37 | return f[n] * rf[k] % mod * rf[n - k] % mod; 38 | } 39 | int main() 40 | { 41 | // https://github.com/bekzhan29/algos 42 | ios_base::sync_with_stdio(0); 43 | cin.tie(0); 44 | 45 | cin >> n >> k; 46 | f[0] = 1; 47 | for (ll i = 1; i <= n; i++) 48 | f[i] = f[i - 1] * i % mod; 49 | rf[n] = bin(f[n], mod - 2); 50 | for (ll i = n - 1; i >= 0; i--) 51 | rf[i] = rf[i + 1] * (i + 1) % mod; 52 | for (ll i = 1; i <= k; i++) 53 | cin >> a[i]; 54 | x[0] = 1; 55 | for (ll i = 1; i <= n; i++) 56 | for (ll j = 1; j < N; j++) 57 | x[j] += x[j - 1], x[j] %= mod; 58 | for (ll i = 0; i <= n; i++) 59 | { 60 | ll cur = c(n, i); 61 | if (i & 1) 62 | cur = mod - cur; 63 | for (ll j = 1; j <= k; j++) 64 | cur *= x[a[j]], cur %= mod; 65 | ans += cur; 66 | ans %= mod; 67 | for (ll i = N - 1; i >= 1; i--) 68 | x[i] += mod - x[i - 1], x[i] %= mod; 69 | for (ll i = 2; i < N; i++) 70 | x[i] += x[i - 2], x[i] %= mod; 71 | } 72 | cout << ans; 73 | } -------------------------------------------------------------------------------- /Data Structures/Segment Tree Beats/segment_tree_beats.cpp: -------------------------------------------------------------------------------- 1 | ll a[N]; 2 | struct item 3 | { 4 | // https://github.com/bekzhan29/algos 5 | ll sum, mx, mn, z; 6 | } t[4 * N]; 7 | item merge(item a, item b) 8 | { 9 | item c; 10 | c.sum = a.sum + b.sum; 11 | c.mx = max(a.mx, b.mx); 12 | c.mn = min(a.mn, b.mn); 13 | c.z = -1; 14 | return c; 15 | } 16 | void push(ll v, ll l, ll r, ll mid) 17 | { 18 | if (t[v].z < 0) 19 | return; 20 | x = t[v].z; 21 | t[v * 2] = {(mid - l + 1) * x, x, x, x}; 22 | t[v * 2 + 1] = {(r - mid) * x, x, x, x}; 23 | t[v].z = -1; 24 | } 25 | void build(ll v, ll l, ll r) 26 | { 27 | if (l == r) 28 | { 29 | t[v] = {a[l], a[l], a[l], -1}; 30 | return; 31 | } 32 | ll mid = (l + r) / 2; 33 | build(v * 2, l, mid); 34 | build(v * 2 + 1, mid + 1, r); 35 | t[v] = merge(t[v * 2], t[v * 2 + 1]); 36 | } 37 | void upd(ll v, ll l, ll r, ll x, ll y, ll k) 38 | { 39 | if (x > y || x > r || y < l || t[v].mx < k) 40 | return; 41 | if (x <= l && r <= y && t[v].mn == t[v].mx) 42 | { 43 | ll cur = t[v].mx % k; 44 | t[v] = {cur * (r - l + 1), cur, cur, cur}; 45 | return; 46 | } 47 | ll mid = (l + r) / 2; 48 | push(v, l, r, mid); 49 | upd(v * 2, l, mid, x, y, k); 50 | upd(v * 2 + 1, mid + 1, r, x, y, k); 51 | t[v] = merge(t[v * 2], t[v * 2 + 1]); 52 | } 53 | void upd2(ll v, ll l, ll r, ll x, ll y) 54 | { 55 | if (l == r) 56 | { 57 | t[v] = {y, y, y, -1}; 58 | return; 59 | } 60 | ll mid = (l + r) / 2; 61 | push(v, l, r, mid); 62 | if (x <= mid) 63 | upd2(v * 2, l, mid, x, y); 64 | else 65 | upd2(v * 2 + 1, mid + 1, r, x, y); 66 | t[v] = merge(t[v * 2], t[v * 2 + 1]); 67 | } 68 | ll sum(ll v, ll l, ll r, ll x, ll y) 69 | { 70 | if (x > y || x > r || y < l) 71 | return 0; 72 | if (x <= l && r <= y) 73 | return t[v].sum; 74 | ll mid = (l + r) / 2; 75 | push(v, l, r, mid); 76 | return sum(v * 2, l, mid, x, y) + sum(v * 2 + 1, mid + 1, r, x, y); 77 | } -------------------------------------------------------------------------------- /Data Structures/Segment Tree/segment_tree2.cpp: -------------------------------------------------------------------------------- 1 | template 2 | struct segment_tree 3 | { 4 | // https://github.com/bekzhan29/algos 5 | private: 6 | int n; 7 | vector tree, z; 8 | void build_tree(int v, int vl, int vr, Type *a) 9 | { 10 | if (vl == vr) 11 | { 12 | tree[v] = a[vl]; 13 | z[v] = 0; 14 | return; 15 | } 16 | int mid = (vl + vr) / 2; 17 | build_tree(v * 2, vl, mid, a); 18 | build_tree(v * 2 + 1, mid + 1, vr, a); 19 | tree[v] = tree[v * 2] + tree[v * 2 + 1]; 20 | z[v] = 0; 21 | } 22 | void push(int v, int vl, int vr, int mid) 23 | { 24 | if (z[v] == 0) 25 | return; 26 | z[v * 2] += z[v]; 27 | z[v * 2 + 1] += z[v]; 28 | tree[v * 2] += z[v] * (mid - vl + 1); 29 | tree[v * 2 + 1] += z[v] * (vr - mid); 30 | z[v] = 0; 31 | } 32 | void upd_tree(int v, int vl, int vr, int l, int r, Type k) 33 | { 34 | if (l > r || l > vr || r < vl) 35 | return; 36 | if (l <= vl && vr <= r) 37 | { 38 | tree[v] += (vr - vl + 1) * k; 39 | z[v] += k; 40 | return; 41 | } 42 | int mid = (vl + vr) / 2; 43 | push(v, vl, vr, mid); 44 | upd_tree(v * 2, vl, mid, l, r, k); 45 | upd_tree(v * 2 + 1, mid + 1, vr, l, r, k); 46 | tree[v] = tree[v * 2] + tree[v * 2 + 1]; 47 | } 48 | Type get_sum(int v, int vl, int vr, int l, int r) 49 | { 50 | if (l > r || l > vr || r < vl) 51 | return 0; 52 | if (l <= vl && vr <= r) 53 | return tree[v]; 54 | int mid = (vl + vr) / 2; 55 | push(v, vl, vr, mid); 56 | return get_sum(v * 2, vl, mid, l, r) + get_sum(v * 2 + 1, mid + 1, vr, l, r); 57 | } 58 | 59 | public: 60 | void build(int n, Type *a) 61 | { 62 | this->n = n; 63 | tree.resize(4 * n); 64 | z.resize(4 * n); 65 | build_tree(1, 1, n, a); 66 | } 67 | void upd(int l, int r, Type k) 68 | { 69 | upd_tree(1, 1, n, l, r, k); 70 | } 71 | Type sum(int l, int r) 72 | { 73 | return get_sum(1, 1, n, l, r); 74 | } 75 | }; -------------------------------------------------------------------------------- /Graph Theory/Heavy Light Decomposition/node_hld.cpp: -------------------------------------------------------------------------------- 1 | template 2 | struct hld 3 | { 4 | // https://github.com/bekzhan29/algos 5 | private: 6 | int n, sz; 7 | vector pr, pos, h, c; 8 | // https://github.com/bekzhan29/algos/blob/master/Data%20Structures/Segment%20Tree/segment_tree1.cpp 9 | segment_tree tree; 10 | vector> v; 11 | void dfs(int x, int par=0) 12 | { 13 | pr[x] = par; 14 | c[x] = 1; 15 | if (par == 0) 16 | pr[x] = x; 17 | for (int to : v[x]) 18 | if (to != par) 19 | dfs(to, x), c[x] += c[to]; 20 | } 21 | void hld_build(int x, int par=0, int head=0) 22 | { 23 | if (!head) 24 | head = x; 25 | h[x] = head; 26 | pos[x] = ++sz; 27 | int mx = 0, u; 28 | for (int to : v[x]) 29 | { 30 | if (to == par) 31 | continue; 32 | if (mx < c[to]) 33 | mx = c[to], u = to; 34 | } 35 | if (mx > 0) 36 | hld_build(u, x, head); 37 | for (int to : v[x]) 38 | { 39 | if (to == par || to == u) 40 | continue; 41 | hld_build(to, x); 42 | } 43 | } 44 | 45 | public: 46 | void init(int n = 1) 47 | { 48 | this->n = n; 49 | sz = 0; 50 | pos.resize(n + 1); 51 | h.resize(n + 1); 52 | c.resize(n + 1); 53 | pr.resize(n + 1); 54 | v.resize(n + 1); 55 | } 56 | void add_edge(int x, int y) 57 | { 58 | v[x].push_back(y); 59 | v[y].push_back(x); 60 | } 61 | void build(Type *a=NULL) 62 | { 63 | dfs(1); 64 | hld_build(1); 65 | tree.build(n); 66 | if (a == NULL) 67 | return; 68 | for (int i = 1; i <= n; i++) 69 | tree.upd(pos[i], a[i]); 70 | } 71 | void upd(int x, Type k) 72 | { 73 | tree.upd(pos[x], k); 74 | } 75 | Type get(int x, int y) 76 | { 77 | Type ans = -INF; 78 | for(;h[x] != h[y]; y = pr[h[y]]) 79 | { 80 | if (pos[h[x]] > pos[h[y]]) 81 | swap(x, y); 82 | ans = max(ans, tree.get(pos[h[y]], pos[y])); 83 | } 84 | if(pos[x] > pos[y]) 85 | swap(x, y); 86 | ans = max(ans, tree.get(pos[x], pos[y])); 87 | return ans; 88 | } 89 | }; -------------------------------------------------------------------------------- /Editorials/IZhO/2017/Hard route/readme.md: -------------------------------------------------------------------------------- 1 | # Крутой маршрут 2 | 3 | ## Подзадачи 1-2 4 | Для начала научимся считать максимальную крутость маршрута. Предположим что мы нашли такие $A$ и $B$ с максимальной крутостью. Теперь допустим что город $C$ находится на максимальномм расстоянии до пути между $A$ и $B$. Скажем что $P$ это город который находится на пути $(A, B)$ и он ближе всего находится к $C$. Заведем функцию $dist(u, v)$ которая возвращает расстояние между городами $u$ и $v$. Теперь если $da=dist(A,P)$, $db=dist(B,P)$ и $dc=dist(C,P)$ тогда сложность пути будет равна $dc*(da+db)$. Также ответ будет максимальным тогда и только тогда когда $dc>=max(da,db)$. 5 | 6 | Теперь мы можем перебрать вершину $P$ и запустить `dfs` из $P$ и для каждой вершины посчитать $dp[v]$ - максимальное расстояние от $v$ до листа в его поддереве. Найдем поддерево $P$ с максимальным значением $dp[v]$ и назовем его $p\_c$, а второй и третий максимальные поддеревья назовем $p\_b$ и $p\_a$. Посчитаем максимальную крутость по всем $P$. 7 | 8 | Чтобы посчитать количество нам необходимо завести счетчик $cnt[v]$ который равен количеству листов в поддереве v на расстоянии $dp[v]$. Скажем что $da=dp[p\_a]$, $db=dp[p\_b]$ и $dc=dp[p\_c]$. Теперь задача разбивается на 4 случая: 9 | 1. $dc > db > da$ 10 | 2. $dc \ge db > da$ 11 | 3. $dc > db \ge da$ 12 | 4. $dc \ge db \ge da$ 13 | 14 | Рассмотрим 4й случай, остальные случаи решаются идентично. Заведем переменную $sum$ и будем идти по поддеревьям $P$, если $dp[v]==dc$ и $dc*(da+db)$ является максимальным ответом, то добавим к ответу $sum*cnt[v]$ и затем сделаем $sum+=cnt[v]$. 15 | 16 | Время работы $O(N^2logN)$ или $O(N^2)$ если не сортировать сыновей $P$. 17 | 18 | ## Полное решение 19 | Нужно ускорить решение сверху. Посчитаем ответ когда $P=1$ и научимся быстро переподвешивать $P$. Когда $P$ переходит в сына вершины $1$, то $dp[v]$ и $cnt[v]$ уже были правильно посчитаны для сыновей $P$, надо узнать максимальное расстояние от предка $P$ до листа и количество таких листов. Это значение можно передать через `dfs` из предка. 20 | 21 | Время работы $O(NlogN)$ или $O(N)$ если не сортировать сыновей $P$. 22 | -------------------------------------------------------------------------------- /Other/Parallel Binary Search/binary_search.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | https://github.com/bekzhan29/algos 3 | */ 4 | #include 5 | using namespace std; 6 | #define pb push_back 7 | #define mp make_pair 8 | #define INF ll(2e15) 9 | #define mod 998244353 10 | #define mod2 1000000007 11 | #define eps 1e-9 12 | #define abs(x) ((x) >= 0 ? (x) : -(x)) 13 | #define y1 solai 14 | #define fi first 15 | #define se second 16 | typedef long long ll; 17 | typedef long double ld; 18 | typedef pair pll; 19 | typedef pair pdd; 20 | typedef pair plll; 21 | mt19937 rng(chrono::steady_clock::now().time_since_epoch().count()); 22 | const ll N = 200100; 23 | ll n, m, x[N], y[N], z[N], a[N], b[N], q, ans[N], l[N], r[N], mid, id[N]; 24 | vector v; 25 | ll p[N], sz[N]; 26 | ll fin(ll a) 27 | { 28 | if (a == p[a]) 29 | return a; 30 | return p[a] = fin(p[a]); 31 | } 32 | bool uni(ll a, ll b) 33 | { 34 | a = fin(a), b = fin(b); 35 | if (a == b) 36 | return 0; 37 | if (sz[a] > sz[b]) 38 | sz[a] += sz[b], p[b] = a; 39 | else 40 | sz[b] += sz[a], p[a] = b; 41 | return 1; 42 | } 43 | void init_dsu(ll n) 44 | { 45 | for (ll i = 1; i <= n; i++) 46 | p[i] = i, sz[i] = 1; 47 | } 48 | bool cmp(ll a, ll b) 49 | { 50 | return l[a] < l[b]; 51 | } 52 | int main() 53 | { 54 | ios_base::sync_with_stdio(0); 55 | cin.tie(0); 56 | 57 | cin >> n >> m; 58 | for (ll i = 1; i <= m; i++) 59 | cin >> a[i] >> b[i]; 60 | cin >> q; 61 | for (ll i = 1; i <= q; i++) 62 | cin >> x[i] >> y[i] >> z[i], v.pb(i), l[i] = 1, r[i] = m, id[i] = i; 63 | for (;;) 64 | { 65 | sort(id + 1, id + q + 1, &cmp); 66 | init_dsu(n); 67 | ll cnt = 0, pos = 0; 68 | for (ll j = 1; j <= q; j++) 69 | { 70 | ll i = id[j]; 71 | mid = (l[i] + r[i]) / 2; 72 | while (pos < mid) 73 | { 74 | pos++; 75 | uni(a[pos], b[pos]); 76 | } 77 | ll calc = sz[fin(x[i])]; 78 | if (fin(x[i]) != fin(y[i])) 79 | calc += sz[fin(y[i])]; 80 | if (calc >= z[i]) 81 | r[i] = mid; 82 | else 83 | l[i] = mid + 1; 84 | if (l[i] == r[i]) 85 | cnt++; 86 | } 87 | if (cnt == q) 88 | break; 89 | } 90 | for (ll i = 1; i <= q; i++) 91 | cout << l[i] << "\n"; 92 | } -------------------------------------------------------------------------------- /Strings/Suffix Array/suffix_array.cpp: -------------------------------------------------------------------------------- 1 | struct suffix_array { 2 | suffix_array (string str) 3 | : original_str(str) { 4 | str += '#'; 5 | int length = str.size(); 6 | 7 | for (int i = 0; i < length; i++) 8 | suffs.push_back(i); 9 | sort(begin(suffs), end(suffs), [&](int lhs, int rhs){ return str[lhs] < str[rhs]; }); 10 | 11 | vector cmp_values(length); 12 | int max_value = 0; 13 | for (int i = 1; i < length; i++) { 14 | if (str[suffs[i]] != str[suffs[i - 1]]) 15 | max_value++; 16 | cmp_values[suffs[i]] = max_value; 17 | } 18 | 19 | auto get_value_pair = [&](int idx, int shift) -> pair { 20 | return {cmp_values[idx], cmp_values[(idx + shift) % length]}; 21 | }; 22 | for (int cmp_len = 1; cmp_len < length; cmp_len *= 2) { 23 | vector counter(max_value + 1); 24 | for (int value: cmp_values) 25 | counter[value]++; 26 | for (int i = 1; i <= max_value; i++) 27 | counter[i] += counter[i - 1]; 28 | vector new_suffs(length); 29 | for (int i = length - 1; i >= 0; i--) { 30 | int prev = (suffs[i] - cmp_len + length) % length; 31 | int prev_value = cmp_values[prev]; 32 | new_suffs[--counter[prev_value]] = prev; 33 | } 34 | suffs.swap(new_suffs); 35 | vector new_values(length); 36 | max_value = 0; 37 | for (int i = 1; i < length; i++) { 38 | pair prev_value = get_value_pair(suffs[i - 1], cmp_len); 39 | pair curr_value = get_value_pair(suffs[i], cmp_len); 40 | if (prev_value != curr_value) 41 | max_value++; 42 | new_values[suffs[i]] = max_value; 43 | } 44 | cmp_values.swap(new_values); 45 | } 46 | // erase '#' symbol from sorted suffix array 47 | suffs.erase(suffs.begin()); 48 | } 49 | 50 | string original_str; 51 | vector suffs; 52 | }; 53 | -------------------------------------------------------------------------------- /Editorials/IZhO/2017/Hard route/english.md: -------------------------------------------------------------------------------- 1 | # Hardest Route 2 | 3 | ## Subtasks 1-2 4 | First, let's learn how to calculate the maximum hardness of a route. Suppose we have found such $A$ and $B$ with the maximum hardness. Now assume that city $C$ is located at the maximum distance to the path between $A$ and $B$. Let us denote $P$ as the city located on the path $(A, B)$ that is closest to $C$. Define a function $dist(u, v)$ that returns the distance between cities $u$ and $v$. Now, if $da=dist(A,P)$, $db=dist(B,P)$, and $dc=dist(C,P)$, then the hardness of the path will equal $dc * (da + db)$. Moreover, the result will be maximized if and only if $dc \geq \max(da, db)$. 5 | 6 | Now we can iterate over the vertex $P$ and perform a `dfs` from $P$. For each vertex, calculate $dp[v]$ - the maximum distance from $v$ to a leaf in its subtree. Find the subtree of $P$ with the maximum $dp[v]$ value and call it $p\_c$, while the second and third maximum subtrees are named $p\_b$ and $p\_a$. Compute the maximum hardness across all $P$. 7 | 8 | To calculate the number of such paths, we need to define a counter $cnt[v]$ which equals the number of leaves in the subtree of $v$ at distance $dp[v]$. Suppose $da=dp[p\_a]$, $db=dp[p\_b]$, and $dc=dp[p\_c]$. The task then splits into four cases: 9 | 1. $dc > db > da$ 10 | 2. $dc \geq db > da$ 11 | 3. $dc > db \geq da$ 12 | 4. $dc \geq db \geq da$ 13 | 14 | Consider the fourth case; the other cases are solved similarly. Define a variable $sum$ and traverse the subtrees of $P$. If $dp[v] == dc$ and $dc \cdot (da + db)$ is equal to the maximum answer, add $sum * cnt[v]$ to the answer, then update $sum += cnt[v]$. 15 | 16 | The runtime is $O(N^2 \log N)$ or $O(N^2)$ if you don't sort the children of $P$. 17 | 18 | ## Full Solution 19 | We need to optimize the solution above. Calculate the answer when $P=1$ and learn to quickly re-hang $P$. When $P$ transitions to a child of vertex $1$, the values of $dp[v]$ and $cnt[v]$ have already been correctly calculated for the children of $P$. It is necessary to determine the maximum distance from the ancestor of $P$ to a leaf and the number of such leaves. This value can be propagated via a `dfs` from the ancestor. 20 | 21 | The runtime is $O(N \log N)$ or $O(N)$ if you don't sort the children of $P$. -------------------------------------------------------------------------------- /Data Structures/Binomial Heap/binomial_heap.cpp: -------------------------------------------------------------------------------- 1 | ll head[N], val[N], sib[N], son[N], par[N], cnt[N], sz; 2 | void print(ll head) 3 | { 4 | while (head) 5 | { 6 | cout << "(" << val[head] << ", " << cnt[head] << ") - "; 7 | head = sib[head]; 8 | } 9 | cout << endl; 10 | } 11 | ll uni(ll x, ll y) 12 | { 13 | // https://github.com/bekzhan29/algos 14 | if (val[x] < val[y]) 15 | swap(x, y); 16 | par[x] = y; 17 | sib[x] = son[y]; 18 | son[y] = x; 19 | cnt[y]++; 20 | return y; 21 | } 22 | ll merge(ll x, ll y) 23 | { 24 | ll head = 0, cur = 0, last = 0, next, go; 25 | if (!x || !y) 26 | return x ? x : y; 27 | if (cnt[x] < cnt[y]) 28 | head = cur = x, x = sib[x]; 29 | else 30 | head = cur = y, y = sib[y]; 31 | while (x && y) 32 | { 33 | if (cnt[x] < cnt[y]) 34 | sib[cur] = x, cur = x, x = sib[x]; 35 | else 36 | sib[cur] = y, cur = y, y = sib[y]; 37 | } 38 | while (x) 39 | sib[cur] = x, cur = x, x = sib[x]; 40 | while (y) 41 | sib[cur] = y, cur = y, y = sib[y]; 42 | cur = head; 43 | while (cur) 44 | { 45 | next = sib[cur]; 46 | while (next && cnt[cur] == cnt[next] && (sib[next] == 0 || cnt[sib[next]] != cnt[next])) 47 | { 48 | go = sib[next]; 49 | cur = uni(cur, next); 50 | if (last > 0) 51 | sib[last] = cur; 52 | else 53 | head = cur; 54 | sib[cur] = go; 55 | next = go; 56 | } 57 | last = cur; 58 | cur = sib[cur]; 59 | } 60 | return head; 61 | } 62 | ll getMin(ll head) 63 | { 64 | ll ans = val[head]; 65 | while (head) 66 | { 67 | ans = min(ans, val[head]); 68 | head = sib[head]; 69 | } 70 | return ans; 71 | } 72 | ll extractMin(ll &head) 73 | { 74 | ll ans = val[head], cur = head, last = 0, pos = head, prev = 0; 75 | while (cur) 76 | { 77 | if (ans > val[cur]) 78 | ans = val[cur], pos = cur, prev = last; 79 | last = cur; 80 | cur = sib[cur]; 81 | } 82 | if (prev == 0) 83 | head = sib[head]; 84 | else 85 | sib[prev] = sib[pos]; 86 | cur = son[pos]; 87 | ll nhead = 0, next; 88 | while (cur) 89 | { 90 | par[cur] = 0; 91 | next = sib[cur]; 92 | sib[cur] = nhead; 93 | nhead = cur; 94 | cur = next; 95 | } 96 | head = merge(head, nhead); 97 | return ans; 98 | } 99 | void add(ll &head, ll x) 100 | { 101 | sz++; 102 | sib[sz] = son[sz] = par[sz] = cnt[sz] = 0; 103 | val[sz] = x; 104 | head = merge(head, sz); 105 | } -------------------------------------------------------------------------------- /Editorials/IZhO/2017/Bootfall/readme.md: -------------------------------------------------------------------------------- 1 | # Бутфол 2 | Во-первых сила Тимы будет целым числом от $1$ до $S$, где $S=\sum a[i]$. Если его сила будет больше чем сумма сил всех остальных игроков, то раунды в которых он участвует не будут ничейными, так как команда в которой будет Тима победит. 3 | 4 | Проверим будет ли раунд в котором Тима снимает на видео ничейным. Во-первых $S$ должно быть четным, во-вторых сила каждой команды должна быть $S/2$. Напишем рюкзак и проверим что можно собрать сумму $S/2$. Если этот раунд не ничейный, то неважно какую силу выберет Тима, поэтому можем вывести $0$ и остановить программу. Иначе надо понять какую силу может выбрать Тима. 5 | 6 | ## Подзадачи 1-3 7 | Заведем массив $ans[x]$, который хранит `true` или `false` в зависимости от того может ли Тима выбрать силу $x$, чтобы все игры были ничейными. Изначально $ans[i]=true$ для всех $i$ от $1$ до $S$. 8 | 9 | Найдем какую силу может выбрать Тима, если игрок $i$ будет снимать на видео. Посчитаем с помощью рюкзака какие сумммы можно собрать без игрока $i$. Заведем новый массив $can[x]$, где будет храниться `true` если игра будет ничейной когда сила Тимы равна $x$ и игрок $i$ снимает на видео, и `false` иначе. Тогда если сила первой команды $x$ и сила второй команды $y$. Тогда Тима должен присоединиться к более слабой команде и выбрать силу $|y-x|$. Тогда раунд будет ничейным. Переберем все возможные $x$, которые можно собрать, тогда $y=S-x-a[i]$ и присвоим $can[|y-x|]=true$. Теперь обновим массив $ans$, если $can[x]=false$, то надо сделать $ans[x]=false$. В конце выведем все значения $x$ для которых $ans[x]=true$. 10 | 11 | Время работы $O(N^2S)$, так как надо посчитать рюкзак для каждого раунда. 12 | 13 | ## Подзадачи 4-5 14 | Приведенное выше решение можно реализовать битсетом. 15 | 16 | Время работы $O(N^2S/64)$. 17 | 18 | ## Полное решение 19 | В рюкзаке $dp[x]$ будет хранить количество способов собрать команду с силой $x$ по какому-то рандомному простому модулю. Посчитаем этот рюкзак для раунда когда Тима снимает на видео. Теперь посчитаем $dp2[x]$ которое хранит количество способов собрать команду с силой $x$ когда игрок $i$ снимает на камеру. Допустим мы уже посчитали $dp2$ для всех чисел от $0$ до $x-1$ и хотим посчитать для $x$. Тогда $dp2[x]=dp[x]-dp2[x-a[i]]$. Почему это так? $dp[x]$ хранит способы в которых есть игрок $i$ и в которых его нет. Сколько есть способов собрать $x$, в которых есть игрок $i$? Их ровно $dp2[x-a[i]]$. Теперь мы можем собрать команду с силой $x$ если $dp2[x]>0$. Идентично прошлому решению считаем массив $can$ и обновляем массив $ans$. Могут быть случаи когда $dp2[x]=0$, но мы можем собрать команду с силой $x$, тогда посчитаем рюкзак по 2 разным модулям. 20 | 21 | Время работы $O(NS)$. -------------------------------------------------------------------------------- /Data Structures/Historic Segment Tree/historic_segment_tree.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | using namespace std; 13 | #define pb push_back 14 | #define INF ll(2e18) 15 | #define mod 998244353 16 | #define eps 1e-9 17 | #define abs(x) ((x) >= 0 ? (x) : -(x)) 18 | #define y1 solai 19 | #define fi first 20 | #define se second 21 | typedef long long ll; 22 | typedef long double ld; 23 | typedef pair pll; 24 | typedef pair pdd; 25 | const ll N = 500100; 26 | ll n, m, a[N], ty, l, r, x; 27 | struct item 28 | { 29 | // https://github.com/bekzhan29/algos 30 | ll x, y, xb, yb; 31 | } t[4 * N]; 32 | item merge(item a, item b) 33 | { 34 | item c = {a.x + b.x, max(a.y + b.x, b.y)}; 35 | c.x = max(c.x, -INF); 36 | c.xb = max({a.xb, a.x + b.xb, c.x}); 37 | c.xb = max(c.xb, -INF); 38 | c.yb = max({a.yb, a.y + b.xb, b.yb, c.y}); 39 | return c; 40 | } 41 | void push(ll v) 42 | { 43 | t[v * 2] = merge(t[v * 2], t[v]); 44 | t[v * 2 + 1] = merge(t[v * 2 + 1], t[v]); 45 | t[v] = {0, -INF, 0, -INF}; 46 | } 47 | void build(ll v, ll l, ll r) 48 | { 49 | if (l == r) 50 | { 51 | t[v] = {0, -INF, 0, -INF}; 52 | return; 53 | } 54 | ll mid = (l + r) / 2; 55 | build(v * 2, l, mid); 56 | build(v * 2 + 1, mid + 1, r); 57 | t[v] = {0, -INF, 0, -INF}; 58 | } 59 | void upd(ll v, ll l, ll r, ll x, ll y, item cur) 60 | { 61 | if (x > y || x > r || y < l) 62 | return; 63 | if (x <= l && r <= y) 64 | { 65 | t[v] = merge(t[v], cur); 66 | return; 67 | } 68 | ll mid = (l + r) / 2; 69 | push(v); 70 | upd(v * 2, l, mid, x, y, cur); 71 | upd(v * 2 + 1, mid + 1, r, x, y, cur); 72 | } 73 | ll get(ll q, ll v, ll l, ll r, ll x) 74 | { 75 | if (l == r) 76 | { 77 | if (q == 1) 78 | return max(a[l] + t[v].x, t[v].y); 79 | return max(a[l] + t[v].xb, t[v].yb); 80 | } 81 | ll mid = (l + r) / 2; 82 | push(v); 83 | if (x <= mid) 84 | return get(q, v * 2, l, mid, x); 85 | return get(q, v * 2 + 1, mid + 1, r, x); 86 | } 87 | int main() 88 | { 89 | ios_base::sync_with_stdio(0); 90 | cin.tie(0); 91 | 92 | cin >> n >> m; 93 | for (ll i = 1; i <= n; i++) 94 | cin >> a[i]; 95 | build(1, 1, n); 96 | for (ll i = 1; i <= m; i++) 97 | { 98 | cin >> ty >> l; 99 | if (ty <= 3) 100 | cin >> r >> x; 101 | if (ty == 1) 102 | upd(1, 1, n, l, r, {x, -INF, 0, -INF}); 103 | if (ty == 2) 104 | upd(1, 1, n, l, r, {-x, 0, 0, -INF}); 105 | if (ty == 3) 106 | upd(1, 1, n, l, r, {-INF, x, 0, -INF}); 107 | if (ty == 4) 108 | cout << get(1, 1, 1, n, l) << "\n"; 109 | if (ty == 5) 110 | cout << get(2, 1, 1, n, l) << "\n"; 111 | } 112 | } -------------------------------------------------------------------------------- /Editorials/IZhO/2017/Bootfall/english.md: -------------------------------------------------------------------------------- 1 | # Bootfall 2 | First, Tima's strength will be an integer from $1$ to $S$, where $S=\sum a[i]$. If his strength is greater than the sum of the strengths of all other players, then the rounds in which he participates will not be draws, since the team in which Tima will be will win. 3 | 4 | Let's check whether the round in which Tima is filming will be a draw. First, $S$ must be even, second, the strength of each team must be $S/2$. Let's write a backpack and check that it is possible to collect the sum $S/2$. If this round is not a draw, then it does not matter what strength Tima chooses, so we can output $0$ and stop the program. Otherwise, we need to understand what strength Tima can choose. 5 | 6 | ## Subtasks 1-3 7 | Let's create an array $ans[x]$, which stores `true` or `false` depending on whether Tima can choose strength $x$, so that all games are draws. Initially $ans[i]=true$ for all $i$ from $1$ to $S$. 8 | 9 | Let's find what strength Tim can choose if player $i$ is filming. Let's use the backpack to calculate what amounts can be collected without player $i$. Let's create a new array $can[x]$, which will store `true` if the game is a draw when Tim's strength is $x$ and player $i$ is filming, and `false` otherwise. Then if the strength of the first team is $x$ and the strength of the second team is $y$. Then Tim should join the weaker team and choose the strength $|y-x|$. Then the round will be a draw. Let's iterate over all possible $x$ that can be collected, then $y=S-x-a[i]$ and set $can[|y-x|]=true$. Now let's update the $ans$ array, if $can[x]=false$, then we need to set $ans[x]=false$. At the end, we will output all the values ​​of $x$ for which $ans[x]=true$. 10 | 11 | The running time is $O(N^2S)$, since we need to calculate the knapsack for each round. 12 | 13 | ## Subtasks 4-5 14 | The solution above can be implemented as a bitset. 15 | 16 | The running time is $O(N^2S/64)$. 17 | 18 | ## Full solution 19 | The knapsack $dp[x]$ will store the number of ways to assemble a team with the strength of $x$ according to some random prime modulo. Let's calculate this knapsack for the round when Tima is filming. Now let's calculate $dp2[x]$ which stores the number of ways to assemble a team with the strength of $x$ when player $i$ is filming. Let's assume that we have already calculated $dp2$ for all numbers from $0$ to $x-1$ and want to calculate for $x$. Then $dp2[x]=dp[x]-dp2[x-a[i]]$. Why is this so? $dp[x]$ stores the ways in which player $i$ is present and in which he is not. How many ways are there to assemble $x$ that contain player $i$? There are exactly $dp2[x-a[i]]$. Now we can assemble a team with strength $x$ if $dp2[x]>0$. Identically to the previous solution, we calculate the $can$ array and update the $ans$ array. There may be cases when $dp2[x]=0$, but we can assemble a team with strength $x$, then we calculate the backpack using 2 different modules. 20 | 21 | The running time is $O(NS)$. -------------------------------------------------------------------------------- /Graph Theory/Disjoint Set Union/readme-kaz.md: -------------------------------------------------------------------------------- 1 | # Қиылыспайтын жиындар 2 | 3 | Қиылыспайтын жиындар екі жиынды біріктіруге және кез келген төбенің қай жиында екенін білуге мүмкіндік береді. 4 | 5 | Алғашында әрбір төбе жеке жиында болады. Бізде $p$ массиві болады, мұндағы $p[i]$ төбе $i$-нің атасы болады. Әр жиынның түбірі $p[i]=i$ болатын төбе болады, яғни оның атасы жоқ. Алғашында барлық $p[i]=i$. 6 | 7 | Төбе $i$ қай жиында екенін табу үшін оның аталары бойынша көтеріліп, түбірін табамыз. Бұл келесі жолмен жүзеге асырылады: 8 | ```cpp 9 | int find_root(int a) 10 | { 11 | if (a == p[a]) 12 | return a; 13 | return find_root(p[a]); 14 | } 15 | ``` 16 | 17 | Мұнда 2 жағдай бар: 18 | 1. Төбе $a$ түбір болып табылады, сондықтан жауапты қайтарамыз. 19 | 2. Әйтпесе, рекурсивті түрде төбе $a$-ның атасы арқылы түбірді табамыз. 20 | 21 | Екі жиынды $a$ және $b$ біріктіру үшін олардың түбірлерін табуымыз керек. Одан кейін бізде тағы 2 жағдай бар: 22 | 1. Олар бір жиында болса, ештеңе істеудің қажеті жоқ. 23 | 2. Әйтпесе, бір төбені екіншісінің атасы етіп, жиындарды біріктіреміз. 24 | ```cpp 25 | bool union_sets(int a, int b) 26 | { 27 | a = find_root(a); 28 | b = find_root(b); 29 | if (a == b) 30 | return 0; 31 | p[b] = a; 32 | return 1; 33 | } 34 | ``` 35 | 36 | Бұл кодтың асимптотикасы төбе $a$-ның қандай биіктікте орналасқанына байланысты. Яғни, жұмыс уақыты $O(h)$, мұндағы $h$ түбірден төбе $a$-ға дейінгі қашықтық. Ең нашар жағдайда бұл $O(N)$ болады. 37 | 38 | Бірінші оңтайландыру түбірге дейінгі жолдарды қысқартудан тұрады. Мұны келесі код арқылы жүзеге асыруға болады: 39 | ```cpp 40 | int find_root(int a) 41 | { 42 | if (a == p[a]) 43 | return a; 44 | return p[a] = find_root(p[a]); 45 | } 46 | ``` 47 | 48 | Екінші оңтайландыруда $b$ төбесін $a$ төбесіне ілудің орнына, біз кіші жиынды үлкен жиынға ілеміз. Ол үшін бізге $r$ жаңа массиві қажет, мұндағы $r[i]$ түбірі $i$ болатын жиынның өлшемін сақтайды. Алғашында барлық $r[i]=1$ деп қоямыз. 49 | ```cpp 50 | bool union_sets(int a, int b) 51 | { 52 | a = find_root(a); 53 | b = find_root(b); 54 | if (a == b) 55 | return 0; 56 | if (r[a] > r[b]) 57 | { 58 | r[a] += r[b]; 59 | p[b] = a; 60 | } 61 | else 62 | { 63 | r[b] += r[a]; 64 | p[a] = b; 65 | } 66 | return 1; 67 | } 68 | ``` 69 | 70 |
71 | dsu.cpp 72 | 73 | 1. Құру $O(N)$ 74 | 2. Жиын түбірін табу $O(1)$ 75 | 3. Жиындарды қосу $O(1)$ 76 |
77 |
78 | dsu_tree.cpp 79 | 80 | 1. Құру $O(NlogN)$ 81 | 2. Память $O(NlogN)$ 82 | 3. Найти компоненту в момент времени $T$ $O(logN)$ 83 | 4. Жиындарды қосу $O(logN)$ 84 | 85 | [Задача](https://atcoder.jp/contests/agc002/tasks/agc002_d) 86 | 87 | [Видео](https://www.youtube.com/watch?v=kHxaTXQfu9E&t=2060s) урок от [Um_nik](https://codeforces.com/profile/Um_nik) 88 |
89 | 90 | 98 | 99 | -------------------------------------------------------------------------------- /Graph Theory/Disjoint Set Union/readme.md: -------------------------------------------------------------------------------- 1 | # Система непересекающихся множеств 2 | 3 | Система непересекающихся множеств позволяет оъединять 2 множества и узнавать в каком множестве находится любая вершина. 4 | 5 | Изначально каждая вершина находится в отдельном множестве. У нас будет массив $p$, где $p[i]$ является предком вершины $i$. Корнем каждого множества будет вершина $i$ такая что $p[i]=i$, то есть у нее нет предка. Сначала все $p[i]=i$. 6 | 7 | Чтобы найти в каком множестве находится вершина $i$ будем подниматься по предкам пока не найдем его. Это можно сделать следующим образом. 8 | ```cpp 9 | int find_root(int a) 10 | { 11 | if (a == p[a]) 12 | return a; 13 | return find_root(p[a]); 14 | } 15 | ``` 16 | 17 | Тут 2 случая: 18 | 1. Вершина $a$ является корнем, тогда возвращаем ответ. 19 | 2. Иначе рекурсивно находим корень через предка вершины $a$. 20 | 21 | Чтобы объединить 2 множества $a$ и $b$ нам надо найти корни их компонент. Далее у нас тоже 2 случая: 22 | 1. Они уже в одной компоненте, тогда нам не нужно ничего делать. 23 | 2. Иначе мы можем сделать одну из вершин предком другой, тем самым объединив их компоненты. 24 | ```cpp 25 | bool union_sets(int a, int b) 26 | { 27 | a = find_root(a); 28 | b = find_root(b); 29 | if (a == b) 30 | return 0; 31 | p[b] = a; 32 | return 1; 33 | } 34 | ``` 35 | 36 | Асимптотика данного кода зависит от высоты на которой находится вершина $a$. То есть время работы $O(h)$, где $h$ это расстояние от корня до вершины $a$. В худшем случае это будет работать за $O(N)$. 37 | 38 | Первая оптимизация состоит в том чтобы сократить пути до корня. Ниже приведен код как это сделать: 39 | ```cpp 40 | int find_root(int a) 41 | { 42 | if (a == p[a]) 43 | return a; 44 | return p[a] = find_root(p[a]); 45 | } 46 | ``` 47 | 48 | Вторая оптимизация состоит в том чтобы вместо подвешивания вершины $b$ за $a$ мы будем подвешивать компоненту с меньшим размером за большую. Для этого нам понадобится новый массив $r$, где $r[i]$ хранит размер компоненты с корнем $i$. Изначально сделаем все $r[i]=1$. 49 | ```cpp 50 | bool union_sets(int a, int b) 51 | { 52 | a = find_root(a); 53 | b = find_root(b); 54 | if (a == b) 55 | return 0; 56 | if (r[a] > r[b]) 57 | { 58 | r[a] += r[b]; 59 | p[b] = a; 60 | } 61 | else 62 | { 63 | r[b] += r[a]; 64 | p[a] = b; 65 | } 66 | return 1; 67 | } 68 | ``` 69 | 70 |
71 | dsu.cpp 72 | 73 | 1. Построение $O(N)$ 74 | 2. Найти компоненту $O(1)$ 75 | 3. Объединить компоненты $O(1)$ 76 |
77 |
78 | dsu_tree.cpp 79 | 80 | 1. Построение $O(NlogN)$ 81 | 2. Память $O(NlogN)$ 82 | 3. Найти компоненту в момент времени $T$ $O(logN)$ 83 | 4. Объединить компоненты $O(logN)$ 84 | 85 | [Задача](https://atcoder.jp/contests/agc002/tasks/agc002_d) 86 | 87 | [Видео](https://www.youtube.com/watch?v=kHxaTXQfu9E&t=2060s) урок от [Um_nik](https://codeforces.com/profile/Um_nik) 88 |
89 | 90 | 98 | 99 | -------------------------------------------------------------------------------- /Graph Theory/Heavy Light Decomposition/edge_hld.cpp: -------------------------------------------------------------------------------- 1 | template 2 | struct hld 3 | { 4 | // https://github.com/bekzhan29/algos 5 | private: 6 | int n, k, cnt, sz, tim; 7 | vector tin, tout, pos, head, ind, l, r, c; 8 | vector tree; 9 | vector> v, pr; 10 | void dfs(int x, int par) 11 | { 12 | tin[x] = ++tim; 13 | pr[0][x] = par; 14 | c[x] = 1; 15 | if (par == 0) 16 | pr[0][x] = x; 17 | for (int i = 1; i < k; i++) 18 | pr[i][x] = pr[i - 1][pr[i - 1][x]]; 19 | for (int to : v[x]) 20 | if (to != par) 21 | dfs(to, x), c[x] += c[to]; 22 | tout[x] = tim; 23 | } 24 | void hld_build(int x, int par) 25 | { 26 | pos[x] = ++sz; 27 | if (head[cnt] == 0) 28 | head[cnt] = x, l[cnt] = pos[x]; 29 | ind[x] = cnt; 30 | r[cnt] = pos[x]; 31 | int mx = 0, u; 32 | for (int to : v[x]) 33 | { 34 | if (to == par) 35 | continue; 36 | if (mx < c[to]) 37 | mx = c[to], u = to; 38 | } 39 | if (mx > 0) 40 | hld_build(u, x); 41 | for (int to : v[x]) 42 | { 43 | if (to == par || to == u) 44 | continue; 45 | cnt++; 46 | hld_build(to, x); 47 | } 48 | } 49 | int find_lca(int x, int y) 50 | { 51 | int lca = x; 52 | for (int i = k - 1; i >= 0; i--) 53 | if (tin[pr[i][lca]] > tin[y] || tout[y] > tout[pr[i][lca]]) 54 | lca = pr[i][lca]; 55 | if (tin[lca] > tin[y] || tout[y] > tout[lca]) 56 | lca = pr[0][lca]; 57 | return lca; 58 | } 59 | void tree_build(int v, int l, int r, Type *a) 60 | { 61 | if (l == r) 62 | { 63 | tree[v] = a[l]; 64 | return; 65 | } 66 | int mid = (l + r) / 2; 67 | tree_build(v * 2, l, mid, a); 68 | tree_build(v * 2 + 1, mid + 1, r, a); 69 | tree[v] = max(tree[v * 2], tree[v * 2 + 1]); 70 | } 71 | void tree_upd(int v, int l, int r, int pos, Type k) 72 | { 73 | if (l == r) 74 | { 75 | tree[v] = k; 76 | return; 77 | } 78 | int mid = (l + r) / 2; 79 | if (pos <= mid) 80 | tree_upd(v * 2, l, mid, pos, k); 81 | else 82 | tree_upd(v * 2 + 1, mid + 1, r, pos, k); 83 | tree[v] = max(tree[v * 2], tree[v * 2 + 1]); 84 | } 85 | Type tree_max(int v, int l, int r, int x, int y) 86 | { 87 | if (x > y || x > r || y < l) 88 | return -INF; 89 | if (x <= l && r <= y) 90 | return tree[v]; 91 | int mid = (l + r) / 2; 92 | return max(tree_max(v * 2, l, mid, x, y), tree_max(v * 2 + 1, mid + 1, r, x, y)); 93 | } 94 | Type hld_max(int x, int y, bool incl = true) 95 | { 96 | Type ans = -INF; 97 | while (ind[y] != ind[x]) 98 | { 99 | ans = max(ans, tree_max(1, 1, n, l[ind[y]], pos[y])); 100 | y = pr[0][head[ind[y]]]; 101 | } 102 | ans = max(ans, tree_max(1, 1, n, pos[x] + !incl, pos[y])); 103 | return ans; 104 | } 105 | 106 | public: 107 | void init(int n = 1) 108 | { 109 | this->n = n; 110 | tin.resize(n + 1); 111 | tout.resize(n + 1); 112 | pos.resize(n + 1); 113 | head.resize(n + 1); 114 | ind.resize(n + 1); 115 | l.resize(n + 1); 116 | r.resize(n + 1); 117 | c.resize(n + 1); 118 | tree.resize(4 * n); 119 | k = 0; 120 | while ((1LL << k) <= n) 121 | k++; 122 | pr.resize(k); 123 | for (int i = 0; i < k; i++) 124 | pr[i].resize(n + 1); 125 | v.resize(n + 1); 126 | cnt = 1; 127 | } 128 | void add_edge(int x, int y) 129 | { 130 | v[x].push_back(y); 131 | v[y].push_back(x); 132 | } 133 | void build(Type *a) 134 | { 135 | dfs(1, 0); 136 | hld_build(1, 0); 137 | tree_build(1, 1, n, a); 138 | } 139 | void upd(int x, int y, Type k) 140 | { 141 | if (tin[x] < tin[y]) 142 | swap(x, y); 143 | tree_upd(1, 1, n, pos[x], k); 144 | } 145 | Type find_max(int x, int y) 146 | { 147 | int lca = find_lca(x, y); 148 | return max(hld_max(lca, x, false), hld_max(lca, y, false)); 149 | } 150 | }; -------------------------------------------------------------------------------- /Data Structures/AVL Tree/avl_tree.cpp: -------------------------------------------------------------------------------- 1 | template 2 | struct node 3 | { 4 | int n, h; 5 | Type val; 6 | node *l, *r; 7 | node(Type key) 8 | { 9 | n = h = 1; 10 | val = key; 11 | l = r = NULL; 12 | } 13 | }; 14 | template 15 | struct avl_tree 16 | { 17 | private: 18 | node *root; 19 | void upd(node *root) 20 | { 21 | int l_height = 0, r_height = 0, lsz = 0, rsz = 0; 22 | if (root->l) 23 | lsz = root->l->n, l_height = root->l->h; 24 | if (root->r) 25 | rsz = root->r->n, r_height = root->r->h; 26 | root->n = lsz + rsz + 1; 27 | root->h = max(l_height, r_height) + 1; 28 | } 29 | node *rotate_r(node *root) 30 | { 31 | node *a = root->l, *b = root; 32 | node *x = a->r; 33 | a->r = b; 34 | b->l = x; 35 | upd(b); 36 | upd(a); 37 | return a; 38 | } 39 | node *rotate_l(node *root) 40 | { 41 | node *a = root->r, *b = root; 42 | node *x = a->l; 43 | a->l = b; 44 | b->r = x; 45 | upd(b); 46 | upd(a); 47 | return a; 48 | } 49 | node *big_rotate_r(node *root) 50 | { 51 | root->l = rotate_l(root->l); 52 | return rotate_r(root); 53 | } 54 | node *big_rotate_l(node *root) 55 | { 56 | root->r = rotate_r(root->r); 57 | return rotate_l(root); 58 | } 59 | node *balance(node *root) 60 | { 61 | 62 | int l_height = 0, r_height = 0; 63 | if (root->l) 64 | l_height = root->l->h; 65 | if (root->r) 66 | r_height = root->r->h; 67 | if (abs(l_height - r_height) <= 1) 68 | return root; 69 | if (l_height > r_height) 70 | { 71 | int llh = 0; 72 | if (root->l->l) 73 | llh = root->l->l->h; 74 | if (llh == root->h - 2) 75 | return rotate_r(root); 76 | else 77 | return big_rotate_r(root); 78 | } 79 | else 80 | { 81 | int rrh = 0; 82 | if (root->r->r) 83 | rrh = root->r->r->h; 84 | if (rrh == root->h - 2) 85 | return rotate_l(root); 86 | else 87 | return big_rotate_l(root); 88 | } 89 | } 90 | node *insert(node *root, Type key) 91 | { 92 | if (root == NULL) 93 | { 94 | root = new node(key); 95 | return root; 96 | } 97 | if (key < root->val) 98 | { 99 | if (!root->l) 100 | root->l = new node(key); 101 | else 102 | root->l = insert(root->l, key); 103 | } 104 | else if (root->val < key) 105 | { 106 | if (!root->r) 107 | root->r = new node(key); 108 | else 109 | root->r = insert(root->r, key); 110 | } 111 | upd(root); 112 | root = balance(root); 113 | return root; 114 | } 115 | node *find_min(node *root) 116 | { 117 | if (!root->l) 118 | return root; 119 | return find_min(root->l); 120 | } 121 | node *erase(node *root, Type key) 122 | { 123 | if (root == NULL) 124 | return NULL; 125 | if (key < root->val) 126 | root->l = erase(root->l, key); 127 | else if (root->val < key) 128 | root->r = erase(root->r, key); 129 | else 130 | { 131 | if (!root->l || !root->r) 132 | return (root->l ? root->l : root->r); 133 | node *r_min = find_min(root->r); 134 | root->r = erase(root->r, r_min->val); 135 | root->val = r_min->val; 136 | } 137 | upd(root); 138 | root = balance(root); 139 | return root; 140 | } 141 | 142 | public: 143 | avl_tree() 144 | { 145 | root = NULL; 146 | } 147 | int height() 148 | { 149 | if (!root) 150 | return 0; 151 | return root->h; 152 | } 153 | int size() 154 | { 155 | if (!root) 156 | return 0; 157 | return root->n; 158 | } 159 | bool empty() 160 | { 161 | return root == NULL; 162 | } 163 | Type begin() 164 | { 165 | assert(root != NULL); 166 | return find_min(root)->val; 167 | } 168 | void insert(Type key) 169 | { 170 | root = insert(root, key); 171 | } 172 | void erase(Type key) 173 | { 174 | root = erase(root, key); 175 | } 176 | }; -------------------------------------------------------------------------------- /Dynamic Programming/Convex Hull Trick/readme.md: -------------------------------------------------------------------------------- 1 | # Трюк выпуклой оболочки 2 | 3 | Трюк выпуклой оболочки это метод оптимизации динамического программирования. В кратце этот трюк помогает обрабатывать 2 типа запросов: 4 | 1. Добавить новую линию $y=k*x+b$ в множество. 5 | 2. Дается $x$ и надо найти такую линию, что значение $k*x+b$ минимально либо максимально. 6 | 7 | Будем считать что нам нужна выпуклая оболочка для подсчета минимального ответа а также что линии из первого запроса даются в порядке убывания $k$ и $x$ из второго запроса даются в порядке увеличения. 8 | 9 | Создадим структуру $line$ для того чтобы хранить линии. 10 | ```cpp 11 | struct line 12 | { 13 | int k, b; 14 | int get(int x) 15 | { 16 | return k * x + b; 17 | } 18 | }; 19 | ``` 20 | 21 | Функция $get(x)$ возвращает значение $y$ для данного $x$. 22 | 23 | Если у нас есть всего одна линия $y = k * x + b$. То эта линия будет оптимальной для любого $x$. 24 | 25 | Если у нас есть 2 линии $y_1 = k_1 * x_1 + b$ и $y_2 = k_2 * x + b_2$, а также $k_1 > k_2$. В этом случае для одних x оптимальной будет первая линия, а для остальных вторая. Но как понять где какая линия лучше? 26 | 27 | На картинке ниже первая линия имеет красный цвет, а вторая линия синий. 28 | 29 | ![](images/convex_hull_2.png) 30 | 31 | Так как эти линии имеют различный угол наклона, то они должны пересечься. Найдем значение $x$ при котором эти линии пересекутся и назовем его $px$, то есть $k_1 * px + b_1 = k_2 * px + b_2$. Получаем $k_1 * px - k_2 * px = b_2 - b_1$, если вынести $px$ наружу, то получим $px * (k_1 - k_2) = b_2 - b_1$. Оставив слева только $px$ получим $px = \dfrac{b_2 - b_1}{k_1 - k_2}$. 32 | 33 | ![](images/convex_hull_3.png) 34 | 35 | Теперь ответ будет следующим: 36 | 1. Для $x < px$ лучшим ответом будет первая линия 37 | 2. Для $x = px$ подойдет любая линия из двух. 38 | 3. Для $x > px$ лучшим ответом будет вторая линия. 39 | 40 | На картинке ниже синим цветом обозначена выпуклая оболочка, которая нам нужна. Оптимальный ответ всегда находится на ней. 41 | 42 | ![](images/convex_hull_4.png) 43 | 44 | Заведем массив $st$ который будет хранить линии из нашей выпуклой оболочки, то есть эти линии являются потенциальным ответом. Хранить размер массива будем в переменной $sz$. Хранить эти линии будем в порядке убывания угла, то есть $k$. Тогда последним элементом массива будет линия с самым минимальным $k$, а первым элементом будет линия с максимальным $k$. 45 | 46 | Предположим что нам надо добавить новую линию в множество. Так как значение $k$ у нее меньше чем у всех предыдущих, то эта линия будет лучше всех остальных для очень больших $x$. Тогда нам необходимо добавить ее в массив. Но при этом какие-то другие линии из выпуклой оболочки могли стать бесполезными и нам надо их удалить. К счастью все линии которые нам надо удалить будут находиться в конце массива. Будем удалять их до тех пор пока это необходимо, затем добавим нашу новую линию. 47 | 48 | Скажем что $line1$ это предпоследняя линия нашей выпуклой оболочки, а $line2$ последняя. Допустим нам надо добавить линию $line3$ и при этом $line1.k > line2.k > line3.k$. Нам надо удалить линию $line2$ если $line1$ и $line3$ лучше чем $line2$ для любого $x$. Для этого найдем значение $x$ при котором $line1$ и $line2$ пересекаются и назовем его $px$, получаем что $px = \dfrac{line2.b - line1.b}{line1.k - line2.k}$. Так же найдем значение где пересекаются $line1$ и $line3$ и назовем его $qx$, тогда $qx = \dfrac{line3.b - line1.b}{line1.k - line3.k}$. Тогда нам надо удалить $line2$ тогда и только тогда, когда $qx \le px$. Ведь для всех $x \le qx$ оптимальной будет $line1$, а для всех $x > qx$ оптимальной будет $line3$. Выходит что $line2$ бесполезна и мы можем удалить ее из выпуклой оболочки. Давайте немного преобразуем неравенство $qx \le px$, это то же самое что $\dfrac{line3.b - line1.b}{line1.k - line3.k} \le \dfrac{line2.b - line1.b}{line1.k - line2.k}$. Так как $line1.k > line2.k > line3.k$ значит $line1.k - line2.k$ это положительное число и $line1.k - line3.k$ тоже. Значит мы спокойно можем перенести их на противоположные стороны и получить $(line3.b - line1.b) \cdot (line1.k - line2.k) \le (line2.b - line1.b) \cdot (line1.k - line3.k)$. **ОЧЕНЬ ВАЖНО** убедиться что вы переносите из деления только положительные числа, иначе если вы перенесете отрицательное число это поменяет знак неравенства. Мы сделали это преобразование для того чтобы избежать деления и проблем с точностью. 49 | 50 | Предположим что наша выпуклая оболочка состоит из линий покрашенных в синий цвет. 51 | 52 | ![](images/convex_hull_1.png) 53 | 54 | Допустим мы хотим добавить красную линию. Так как $px < qx$, то ничего удалять не надо. Просто добавим красную линию в нашу выпуклую оболочку. 55 | 56 | ![](images/convex_hull_5.png) 57 | 58 | Новая оболочка будет выглядеть следующим образом. 59 | 60 | ![](images/convex_hull_6.png) 61 | 62 | Рассмотрим другой пример, новая линия обозначена красным цветом. Так как в этот раз qx <= px нам придется удалить последнюю линию из выпуклой оболочки и добавить красную. 63 | 64 | ![](images/convex_hull_7.png) 65 | 66 | Новая оболочка будет выглядеть следующим образом. 67 | 68 | ![](images/convex_hull_8.png) 69 | 70 | Ниже приведен код который это делает. 71 | ```cpp 72 | cin >> k >> b; 73 | new_line = line(k, b); 74 | while(sz > 1 && (new_line.b - st[sz - 1].b) * (st[sz - 1].k - st[sz].k) <= (st[sz].b - st[sz - 1].b) * (st[sz - 1].k - new_line.k)) 75 | sz--; 76 | st[++sz] = new_line; 77 | r = min(r, sz); 78 | ``` 79 | 80 | Теперь чтобы найти лучшую линию для определенного $x$ заметим следующее. Заведем функцию $\verb|best_line|(x)$ которая возвращает позицию линии в стэке которая дает оптимальный ответ для этого $x$. Тогда заметим что $\verb|best_line|(x) \le \verb|best_line|(x + 1)$, то есть чем больше $x$ тем выше в стэке будет находиться лучшая для него линия. Если $x$ из второго типа запроса даются в случайном порядке, тогда мы можем сделать бинарный поиск по стэку и найти лучшую линию. А если $x$ даются в порядке возрастания, то мы можем завести переменную $r$ которая хранит позицию самой оптимальной линии для $x$ из последнего запроса второго типа. Изначально когда у нас нет линий в стэке $r$ будет равно $0$. Теперь когда нас просят найти ответ для нового $x$ будем пытаться увеличивать $r$ пока ответ улучшается. 81 | ```cpp 82 | while (r < sz && st[r].get(x) >= st[r + 1].get(x)) 83 | r++; 84 | cout << st[r].get(x) << "\n"; 85 | ``` 86 | 87 | В итоге методом амортизационного анализа получаем что запросы первого и второго типа работают за $O(1)$. 88 | 89 | Видео: 90 | 1. [Лекция от Станкевича](https://www.youtube.com/watch?v=ou0phsO_cLo) 91 | 92 | Задачи: 93 | 1. [Land acquisition](https://vjudge.net/problem/SPOJ-ACQUIRE) 94 | 95 | 96 | --------------------------------------------------------------------------------