From 8e180758332a877c4e7eff475e5f921192b913b9 Mon Sep 17 00:00:00 2001 From: Atulbedwal <76045376+Atulbedwal@users.noreply.github.com> Date: Tue, 25 Jun 2024 00:57:42 +0530 Subject: [PATCH 01/26] Update strong-orientation.md --- src/graph/strong-orientation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/graph/strong-orientation.md b/src/graph/strong-orientation.md index 50397b287..69a7b37d9 100644 --- a/src/graph/strong-orientation.md +++ b/src/graph/strong-orientation.md @@ -78,7 +78,7 @@ void find_bridges(int v) { bridge_cnt++; } } else { - low[v] = min(low[v], low[nv]); + low[v] = min(low[v], tin[nv]); } } } From 670cbaa2c8cfd742993a43542258f8ebd6a320c8 Mon Sep 17 00:00:00 2001 From: Mirco Paul <63196848+Electron1997@users.noreply.github.com> Date: Thu, 6 Feb 2025 22:26:53 +0100 Subject: [PATCH 02/26] Add practice problems Add some Burnside lemma practice problems sorted by difficulty --- src/combinatorics/burnside.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/combinatorics/burnside.md b/src/combinatorics/burnside.md index fa799399c..894b1e87d 100644 --- a/src/combinatorics/burnside.md +++ b/src/combinatorics/burnside.md @@ -266,3 +266,8 @@ int solve(int n, int m) { return sum / s.size(); } ``` +## Practice Problems +* [CSES - Counting Necklaces](https://cses.fi/problemset/task/2209) +* [CSES - Counting Grids](https://cses.fi/problemset/task/2210) +* [Codeforces - Buildings](https://codeforces.com/gym/101873/problem/B) +* [CS Academy - Cube Coloring](https://csacademy.com/contest/beta-round-8/task/cube-coloring/) From dc29826f28c7960efd657816a6dd4af22caf0ac7 Mon Sep 17 00:00:00 2001 From: CDTheGod <158807586+CDTheGod@users.noreply.github.com> Date: Wed, 5 Mar 2025 18:39:38 +0530 Subject: [PATCH 03/26] Update divide-and-conquer-dp.md --- src/dynamic_programming/divide-and-conquer-dp.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dynamic_programming/divide-and-conquer-dp.md b/src/dynamic_programming/divide-and-conquer-dp.md index 457e17c24..a33cc7b45 100644 --- a/src/dynamic_programming/divide-and-conquer-dp.md +++ b/src/dynamic_programming/divide-and-conquer-dp.md @@ -113,7 +113,7 @@ both! - [SPOJ - LARMY](https://www.spoj.com/problems/LARMY/) - [SPOJ - NKLEAVES](https://www.spoj.com/problems/NKLEAVES/) - [Timus - Bicolored Horses](https://acm.timus.ru/problem.aspx?space=1&num=1167) -- [USACO - Circular Barn](http://www.usaco.org/index.php?page=viewproblem2&cpid=616) +- [USACO - Circular Barn](https://usaco.org/index.php?page=viewproblem2&cpid=626) - [UVA - Arranging Heaps](https://onlinejudge.org/external/125/12524.pdf) - [UVA - Naming Babies](https://onlinejudge.org/external/125/12594.pdf) From b861d1ec80d7e08ff7ebfc71c8cf262db65cf346 Mon Sep 17 00:00:00 2001 From: Franklin Date: Sat, 8 Mar 2025 02:54:08 -0500 Subject: [PATCH 04/26] Update topological-sort.md In the implementation example, the if statement within the dfs function is missing a pair of brackets. --- src/graph/topological-sort.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/graph/topological-sort.md b/src/graph/topological-sort.md index f8e8e9218..acfad5331 100644 --- a/src/graph/topological-sort.md +++ b/src/graph/topological-sort.md @@ -65,8 +65,9 @@ vector ans; void dfs(int v) { visited[v] = true; for (int u : adj[v]) { - if (!visited[u]) + if (!visited[u]) { dfs(u); + } } ans.push_back(v); } From 2c8dc7b96a5bd484eaa1715656ef6d474abf352c Mon Sep 17 00:00:00 2001 From: Jakob Kogler Date: Sun, 9 Mar 2025 13:30:00 +0100 Subject: [PATCH 05/26] Remove deprecated tags plugin flag --- mkdocs.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mkdocs.yml b/mkdocs.yml index ed15e7068..60f512bc6 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -61,8 +61,7 @@ plugins: hooks: on_env: "hooks:on_env" - search - - tags: - tags_file: tags.md + - tags - literate-nav: nav_file: navigation.md - git-revision-date-localized: From bbe3477bc857e34ad82dad40a8442da3a7204ab7 Mon Sep 17 00:00:00 2001 From: Jakob Kogler Date: Sun, 9 Mar 2025 13:43:34 +0100 Subject: [PATCH 06/26] Make tags index work again --- src/tags.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tags.md b/src/tags.md index c462960cd..6759f272e 100644 --- a/src/tags.md +++ b/src/tags.md @@ -2,4 +2,4 @@ This file contains a global index of all tags used on the pages. -[TAGS] \ No newline at end of file + From 42952998071723361d0ec88314183af6f35f2fd6 Mon Sep 17 00:00:00 2001 From: "Yurii A." Date: Mon, 24 Mar 2025 21:49:18 +0200 Subject: [PATCH 07/26] Manacher's algorithm testcases (#1393) * Fix inconsistencies in Manacher's algorithm * Add test for manacher_odd * Update test_manacher_odd.cpp * empty commit to run tests? --------- Co-authored-by: Yurii A. Co-authored-by: Oleksandr Kulkov --- src/string/manacher.md | 8 ++--- test/test_manacher_odd.cpp | 74 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 4 deletions(-) create mode 100644 test/test_manacher_odd.cpp diff --git a/src/string/manacher.md b/src/string/manacher.md index 2c7cb35b6..0c8bd5928 100644 --- a/src/string/manacher.md +++ b/src/string/manacher.md @@ -49,7 +49,7 @@ Such an algorithm is slow, it can calculate the answer only in $O(n^2)$. The implementation of the trivial algorithm is: ```cpp -vector manacher_odd(string s) { +vector manacher_odd_trivial(string s) { int n = s.size(); s = "$" + s + "^"; vector p(n + 2); @@ -68,7 +68,7 @@ Terminal characters `$` and `^` were used to avoid dealing with ends of the stri We describe the algorithm to find all the sub-palindromes with odd length, i. e. to calculate $d_{odd}[]$. -For fast calculation we'll maintain the **borders $(l, r)$** of the rightmost found (sub-)palindrome (i. e. the current rightmost (sub-)palindrome is $s[l+1] s[l+2] \dots s[r-1]$). Initially we set $l = 0, r = 1$, which corresponds to the empty string. +For fast calculation we'll maintain the **exclusive borders $(l, r)$** of the rightmost found (sub-)palindrome (i. e. the current rightmost (sub-)palindrome is $s[l+1] s[l+2] \dots s[r-1]$). Initially we set $l = 0, r = 1$, which corresponds to the empty string. So, we want to calculate $d_{odd}[i]$ for the next $i$, and all the previous values in $d_{odd}[]$ have been already calculated. We do the following: @@ -140,12 +140,12 @@ For calculating $d_{odd}[]$, we get the following code. Things to note: - The while loop denotes the trivial algorithm. We launch it irrespective of the value of $k$. - If the size of palindrome centered at $i$ is $x$, then $d_{odd}[i]$ stores $\frac{x+1}{2}$. -```cpp +```{.cpp file=manacher_odd} vector manacher_odd(string s) { int n = s.size(); s = "$" + s + "^"; vector p(n + 2); - int l = 1, r = 1; + int l = 0, r = 1; for(int i = 1; i <= n; i++) { p[i] = max(0, min(r - i, p[l + (r - i)])); while(s[i - p[i]] == s[i + p[i]]) { diff --git a/test/test_manacher_odd.cpp b/test/test_manacher_odd.cpp new file mode 100644 index 000000000..cd46b7488 --- /dev/null +++ b/test/test_manacher_odd.cpp @@ -0,0 +1,74 @@ +#include +using namespace std; + +#include "manacher_odd.h" + +string getRandomString(size_t n, uint32_t seed, char minLetter='a', char maxLetter='b') { + assert(minLetter <= maxLetter); + const size_t nLetters = static_cast(maxLetter) - static_cast(minLetter) + 1; + std::uniform_int_distribution distr(0, nLetters - 1); + std::mt19937 gen(seed); + + string res; + res.reserve(n); + + for (size_t i = 0; i < n; ++i) + res.push_back('a' + distr(gen)); + + return res; +} + +bool testManacherOdd(const std::string &s) { + const auto n = s.size(); + const auto d_odd = manacher_odd(s); + + if (d_odd.size() != n) + return false; + + const auto inRange = [&](size_t idx) { + return idx >= 0 && idx < n; + }; + + for (size_t i = 0; i < n; ++i) { + if (d_odd[i] < 0) + return false; + for (int d = 0; d < d_odd[i]; ++d) { + const auto idx1 = i - d; + const auto idx2 = i + d; + + if (!inRange(idx1) || !inRange(idx2)) + return false; + if (s[idx1] != s[idx2]) + return false; + } + + const auto idx1 = i - d_odd[i]; + const auto idx2 = i + d_odd[i]; + if (inRange(idx1) && inRange(idx2) && s[idx1] == s[idx2]) + return false; + } + + return true; +} + +int main() { + vector testCases; + + testCases.push_back(""); + for (size_t i = 1; i <= 25; ++i) { + auto s = string{}; + s.resize(i, 'a'); + testCases.push_back(move(s)); + } + testCases.push_back("abba"); + testCases.push_back("abccbaasd"); + for (size_t n = 9; n <= 100; n += 10) + testCases.push_back(getRandomString(n, /* seed */ n, 'a', 'd')); + for (size_t n = 7; n <= 100; n += 10) + testCases.push_back(getRandomString(n, /* seed */ n)); + + for (const auto &s: testCases) + assert(testManacherOdd(s)); + + return 0; +} From 82a06ff7246bc161dfc6a176f2adf79dba19f72b Mon Sep 17 00:00:00 2001 From: Yury Semenov Date: Mon, 24 Mar 2025 23:15:38 +0300 Subject: [PATCH 08/26] Added a paragraph on golden section search (#1119) * . * Update ternary_search.md --------- Co-authored-by: Oleksandr Kulkov --- src/num_methods/ternary_search.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/num_methods/ternary_search.md b/src/num_methods/ternary_search.md index 59afaaa82..7e73c7771 100644 --- a/src/num_methods/ternary_search.md +++ b/src/num_methods/ternary_search.md @@ -57,6 +57,20 @@ If $f(x)$ takes integer parameter, the interval $[l, r]$ becomes discrete. Since The difference occurs in the stopping criterion of the algorithm. Ternary search will have to stop when $(r - l) < 3$, because in that case we can no longer select $m_1$ and $m_2$ to be different from each other as well as from $l$ and $r$, and this can cause an infinite loop. Once $(r - l) < 3$, the remaining pool of candidate points $(l, l + 1, \ldots, r)$ needs to be checked to find the point which produces the maximum value $f(x)$. +### Golden section search + +In some cases computing $f(x)$ may be quite slow, but reducing the number of iterations is infeasible due to precision issues. Fortunately, it is possible to compute $f(x)$ only once at each iteration (except the first one). + +To see how to do this, let's revisit the selection method for $m_1$ and $m_2$. Suppose that we select $m_1$ and $m_2$ on $[l, r]$ in such a way that $\frac{r - l}{r - m_1} = \frac{r - l}{m_2 - l} = \varphi$ where $\varphi$ is some constant. In order to reduce the amount of computations, we want to select such $\varphi$ that on the next iteration one of the new evaluation points $m_1'$, $m_2'$ will coincide with either $m_1$ or $m_2$, so that we can reuse the already computed function value. + +Now suppose that after the current iteration we set $l = m_1$. Then the point $m_1'$ will satisfy $\frac{r - m_1}{r - m_1'} = \varphi$. We want this point to coincide with $m_2$, meaning that $\frac{r - m_1}{r - m_2} = \varphi$. + +Multiplying both sides of $\frac{r - m_1}{r - m_2} = \varphi$ by $\frac{r - m_2}{r - l}$ we obtain $\frac{r - m_1}{r - l} = \varphi\frac{r - m_2}{r - l}$. Note that $\frac{r - m_1}{r - l} = \frac{1}{\varphi}$ and $\frac{r - m_2}{r - l} = \frac{r - l + l - m_2}{r - l} = 1 - \frac{1}{\varphi}$. Substituting that and multiplying by $\varphi$, we obtain the following equation: + +$\varphi^2 - \varphi - 1 = 0$ + +This is a well-known golden section equation. Solving it yields $\frac{1 \pm \sqrt{5}}{2}$. Since $\varphi$ must be positive, we obtain $\varphi = \frac{1 + \sqrt{5}}{2}$. By applying the same logic to the case when we set $r = m_2$ and want $m_2'$ to coincide with $m_1$, we obtain the same value of $\varphi$ as well. So, if we choose $m_1 = l + \frac{r - l}{1 + \varphi}$ and $m_2 = r - \frac{r - l}{1 + \varphi}$, on each iteration we can re-use one of the values $f(x)$ computed on the previous iteration. + ## Implementation ```cpp @@ -81,6 +95,7 @@ Here `eps` is in fact the absolute error (not taking into account errors due to Instead of the criterion `r - l > eps`, we can select a constant number of iterations as a stopping criterion. The number of iterations should be chosen to ensure the required accuracy. Typically, in most programming challenges the error limit is ${10}^{-6}$ and thus 200 - 300 iterations are sufficient. Also, the number of iterations doesn't depend on the values of $l$ and $r$, so the number of iterations corresponds to the required relative error. ## Practice Problems + - [Codeforces - New Bakery](https://codeforces.com/problemset/problem/1978/B) - [Codechef - Race time](https://www.codechef.com/problems/AMCS03) - [Hackerearth - Rescuer](https://www.hackerearth.com/problem/algorithm/rescuer-2d2495cb/) @@ -95,5 +110,8 @@ Instead of the criterion `r - l > eps`, we can select a constant number of itera * [Codeforces - Devu and his Brother](https://codeforces.com/problemset/problem/439/D) * [Codechef - Is This JEE ](https://www.codechef.com/problems/ICM2003) * [Codeforces - Restorer Distance](https://codeforces.com/contest/1355/problem/E) +* [TIMUS 1058 Chocolate](https://acm.timus.ru/problem.aspx?space=1&num=1058) +* [TIMUS 1436 Billboard](https://acm.timus.ru/problem.aspx?space=1&num=1436) +* [TIMUS 1451 Beerhouse Tale](https://acm.timus.ru/problem.aspx?space=1&num=1451) * [TIMUS 1719 Kill the Shaitan-Boss](https://acm.timus.ru/problem.aspx?space=1&num=1719) * [TIMUS 1913 Titan Ruins: Alignment of Forces](https://acm.timus.ru/problem.aspx?space=1&num=1913) From e24c67450d38ffdde84b1191a85f9c4b1687d054 Mon Sep 17 00:00:00 2001 From: FloppaInspector <136809316+FloppaInspector@users.noreply.github.com> Date: Sun, 6 Apr 2025 00:39:54 +0900 Subject: [PATCH 09/26] Update knuth-optimization.md In time complexity proof cancellation explanation, j=N -> j=N-1 --- src/dynamic_programming/knuth-optimization.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dynamic_programming/knuth-optimization.md b/src/dynamic_programming/knuth-optimization.md index 535c7caf2..35978b839 100644 --- a/src/dynamic_programming/knuth-optimization.md +++ b/src/dynamic_programming/knuth-optimization.md @@ -77,7 +77,7 @@ $$ \sum\limits_{i=1}^N \sum\limits_{j=i}^{N-1} [opt(i+1,j+1)-opt(i,j)]. $$ -As you see, most of the terms in this expression cancel each other out, except for positive terms with $j=N$ and negative terms with $i=1$. Thus, the whole sum can be estimated as +As you see, most of the terms in this expression cancel each other out, except for positive terms with $j=N-1$ and negative terms with $i=1$. Thus, the whole sum can be estimated as $$ \sum\limits_{k=1}^N[opt(k,N)-opt(1,k)] = O(n^2), From 1e3a93051eb22ad0238d3265246b650ce8a8954b Mon Sep 17 00:00:00 2001 From: Oleksandr Kulkov Date: Tue, 8 Apr 2025 11:45:34 +0200 Subject: [PATCH 10/26] Remove navigation tabs, add toggle to hide sidebar (#1440) --- .github/workflows/build.yml | 2 +- mkdocs.yml | 3 ++- scripts/install-mkdocs.sh | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e820830eb..37a634b16 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,7 +19,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v5.2.0 with: - python-version: '3.8' + python-version: '3.11' - name: Install mkdocs-material run: | scripts/install-mkdocs.sh diff --git a/mkdocs.yml b/mkdocs.yml index 60f512bc6..466463564 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -22,7 +22,6 @@ theme: icon: repo: fontawesome/brands/github features: - - navigation.tabs - toc.integrate - search.suggest - content.code.copy @@ -57,6 +56,8 @@ markdown_extensions: permalink: true plugins: + - toggle-sidebar: + toggle_button: all - mkdocs-simple-hooks: hooks: on_env: "hooks:on_env" diff --git a/scripts/install-mkdocs.sh b/scripts/install-mkdocs.sh index 4202c9a97..9c4e73c3f 100755 --- a/scripts/install-mkdocs.sh +++ b/scripts/install-mkdocs.sh @@ -2,6 +2,7 @@ pip install \ "mkdocs-material>=9.0.2" \ + mkdocs-toggle-sidebar-plugin \ mkdocs-macros-plugin \ mkdocs-literate-nav \ mkdocs-git-authors-plugin \ From 21299e74b39389acb6bf8ad257d65cc1875ffa93 Mon Sep 17 00:00:00 2001 From: Michael Hayter Date: Mon, 14 Apr 2025 22:32:08 -0400 Subject: [PATCH 11/26] add_script updates? --- src/algebra/factorization.md | 4 +++- src/algebra/sieve-of-eratosthenes.md | 4 +++- src/data_structures/fenwick.md | 4 +++- src/geometry/basic-geometry.md | 16 ++++++++++++---- src/geometry/convex_hull_trick.md | 8 ++++++-- src/geometry/delaunay.md | 4 +++- src/geometry/intersecting_segments.md | 12 +++++++++--- src/geometry/lattice-points.md | 8 ++++++-- src/geometry/manhattan-distance.md | 8 ++++++-- src/geometry/minkowski.md | 4 +++- src/geometry/planar.md | 8 ++++++-- src/geometry/point-location.md | 8 ++++++-- src/geometry/tangents-to-two-circles.md | 4 +++- src/geometry/vertical_decomposition.md | 4 +++- src/graph/2SAT.md | 8 ++++++-- src/graph/edmonds_karp.md | 16 ++++++++++++---- src/graph/hld.md | 4 +++- src/graph/lca.md | 4 +++- src/graph/lca_farachcoltonbender.md | 4 +++- src/graph/rmq_linear.md | 8 ++++++-- src/graph/topological-sort.md | 6 +++--- src/others/stern_brocot_tree_farey_sequences.md | 4 +++- src/others/tortoise_and_hare.md | 12 +++++++++--- 23 files changed, 120 insertions(+), 42 deletions(-) diff --git a/src/algebra/factorization.md b/src/algebra/factorization.md index 58a43b961..11bf4049c 100644 --- a/src/algebra/factorization.md +++ b/src/algebra/factorization.md @@ -246,7 +246,9 @@ If $p$ is smaller than $\sqrt{n}$, the repetition will likely start in $O(\sqrt[ Here is a visualization of such a sequence $\{x_i \bmod p\}$ with $n = 2206637$, $p = 317$, $x_0 = 2$ and $f(x) = x^2 + 1$. From the form of the sequence you can see very clearly why the algorithm is called Pollard's $\rho$ algorithm. -
![Pollard's rho visualization](pollard_rho.png)
+
+ Pollard's rho visualization +
Yet, there is still an open question. How can we exploit the properties of the sequence $\{x_i \bmod p\}$ to our advantage without even knowing the number $p$ itself? diff --git a/src/algebra/sieve-of-eratosthenes.md b/src/algebra/sieve-of-eratosthenes.md index a07e6dc02..560d176e7 100644 --- a/src/algebra/sieve-of-eratosthenes.md +++ b/src/algebra/sieve-of-eratosthenes.md @@ -19,7 +19,9 @@ And we continue this procedure until we have processed all numbers in the row. In the following image you can see a visualization of the algorithm for computing all prime numbers in the range $[1; 16]$. It can be seen, that quite often we mark numbers as composite multiple times. -
![Sieve of Eratosthenes](sieve_eratosthenes.png)
+
+ Sieve of Eratosthenes +
The idea behind is this: A number is prime, if none of the smaller prime numbers divides it. diff --git a/src/data_structures/fenwick.md b/src/data_structures/fenwick.md index 82755b452..439885b83 100644 --- a/src/data_structures/fenwick.md +++ b/src/data_structures/fenwick.md @@ -128,7 +128,9 @@ where $|$ is the bitwise OR operator. The following image shows a possible interpretation of the Fenwick tree as tree. The nodes of the tree show the ranges they cover. -
![Binary Indexed Tree](binary_indexed_tree.png)
+
+ Binary Indexed Tree +
## Implementation diff --git a/src/geometry/basic-geometry.md b/src/geometry/basic-geometry.md index 4c052a784..89acb1642 100644 --- a/src/geometry/basic-geometry.md +++ b/src/geometry/basic-geometry.md @@ -112,7 +112,9 @@ The dot (or scalar) product $\mathbf a \cdot \mathbf b$ for vectors $\mathbf a$ Geometrically it is product of the length of the first vector by the length of the projection of the second vector onto the first one. As you may see from the image below this projection is nothing but $|\mathbf a| \cos \theta$ where $\theta$ is the angle between $\mathbf a$ and $\mathbf b$. Thus $\mathbf a\cdot \mathbf b = |\mathbf a| \cos \theta \cdot |\mathbf b|$. -
![](https://upload.wikimedia.org/wikipedia/commons/thumb/3/3e/Dot_Product.svg/300px-Dot_Product.svg.png)
+
+ +
The dot product holds some notable properties: @@ -181,7 +183,9 @@ To see the next important property we should take a look at the set of points $\ You can see that this set of points is exactly the set of points for which the projection onto $\mathbf a$ is the point $C \cdot \dfrac{\mathbf a}{|\mathbf a|}$ and they form a hyperplane orthogonal to $\mathbf a$. You can see the vector $\mathbf a$ alongside with several such vectors having same dot product with it in 2D on the picture below: -
![Vectors having same dot product with a](https://i.imgur.com/eyO7St4.png)
+
+ Vectors having same dot product with a +
In 2D these vectors will form a line, in 3D they will form a plane. Note that this result allows us to define a line in 2D as $\mathbf r\cdot \mathbf n=C$ or $(\mathbf r - \mathbf r_0)\cdot \mathbf n=0$ where $\mathbf n$ is vector orthogonal to the line and $\mathbf r_0$ is any vector already present on the line and $C = \mathbf r_0\cdot \mathbf n$. @@ -192,14 +196,18 @@ In the same manner a plane can be defined in 3D. ### Definition Assume you have three vectors $\mathbf a$, $\mathbf b$ and $\mathbf c$ in 3D space joined in a parallelepiped as in the picture below: -
![Three vectors](https://upload.wikimedia.org/wikipedia/commons/thumb/3/3e/Parallelepiped_volume.svg/240px-Parallelepiped_volume.svg.png)
+
+ Three vectors +
How would you calculate its volume? From school we know that we should multiply the area of the base with the height, which is projection of $\mathbf a$ onto direction orthogonal to base. That means that if we define $\mathbf b \times \mathbf c$ as the vector which is orthogonal to both $\mathbf b$ and $\mathbf c$ and which length is equal to the area of the parallelogram formed by $\mathbf b$ and $\mathbf c$ then $|\mathbf a\cdot (\mathbf b\times\mathbf c)|$ will be equal to the volume of the parallelepiped. For integrity we will say that $\mathbf b\times \mathbf c$ will be always directed in such way that the rotation from the vector $\mathbf b$ to the vector $\mathbf c$ from the point of $\mathbf b\times \mathbf c$ is always counter-clockwise (see the picture below). -
![cross product](https://upload.wikimedia.org/wikipedia/commons/thumb/b/b0/Cross_product_vector.svg/250px-Cross_product_vector.svg.png)
+
+ cross product +
This defines the cross (or vector) product $\mathbf b\times \mathbf c$ of the vectors $\mathbf b$ and $\mathbf c$ and the triple product $\mathbf a\cdot(\mathbf b\times \mathbf c)$ of the vectors $\mathbf a$, $\mathbf b$ and $\mathbf c$. diff --git a/src/geometry/convex_hull_trick.md b/src/geometry/convex_hull_trick.md index f9c938fb1..052073e2c 100644 --- a/src/geometry/convex_hull_trick.md +++ b/src/geometry/convex_hull_trick.md @@ -17,7 +17,9 @@ The idea of this approach is to maintain a lower convex hull of linear functions Actually it would be a bit more convenient to consider them not as linear functions, but as points $(k;b)$ on the plane such that we will have to find the point which has the least dot product with a given point $(x;1)$, that is, for this point $kx+b$ is minimized which is the same as initial problem. Such minimum will necessarily be on lower convex envelope of these points as can be seen below: -
![lower convex hull](convex_hull_trick.png)
+
+ lower convex hull +
One has to keep points on the convex hull and normal vectors of the hull's edges. When you have a $(x;1)$ query you'll have to find the normal vector closest to it in terms of angles between them, then the optimum linear function will correspond to one of its endpoints. @@ -90,7 +92,9 @@ Assume we're in some vertex corresponding to half-segment $[l,r)$ and the functi Here is the illustration of what is going on in the vertex when we add new function: -
![Li Chao Tree vertex](li_chao_vertex.png)
+
+ Li Chao Tree vertex +
Let's go to implementation now. Once again we will use complex numbers to keep linear functions. diff --git a/src/geometry/delaunay.md b/src/geometry/delaunay.md index c5f5c22d1..68d67ce8f 100644 --- a/src/geometry/delaunay.md +++ b/src/geometry/delaunay.md @@ -30,7 +30,9 @@ Because of the duality, we only need a fast algorithm to compute only one of $V$ ## Quad-edge data structure During the algorithm $D$ will be stored inside the quad-edge data structure. This structure is described in the picture: -
![Quad-Edge](quad-edge.png)
+
+ Quad-Edge +
In the algorithm we will use the following functions on edges: diff --git a/src/geometry/intersecting_segments.md b/src/geometry/intersecting_segments.md index a5eba5f6b..ac26c8fd5 100644 --- a/src/geometry/intersecting_segments.md +++ b/src/geometry/intersecting_segments.md @@ -16,18 +16,24 @@ The naive solution algorithm is to iterate over all pairs of segments in $O(n^2) Let's draw a vertical line $x = -\infty$ mentally and start moving this line to the right. In the course of its movement, this line will meet with segments, and at each time a segment intersect with our line it intersects in exactly one point (we will assume that there are no vertical segments). -
![sweep line and line segment intersection](sweep_line_1.png)
+
+ sweep line and line segment intersection +
Thus, for each segment, at some point in time, its point will appear on the sweep line, then with the movement of the line, this point will move, and finally, at some point, the segment will disappear from the line. We are interested in the **relative order of the segments** along the vertical. Namely, we will store a list of segments crossing the sweep line at a given time, where the segments will be sorted by their $y$-coordinate on the sweep line. -
![relative order of the segments across sweep line](sweep_line_2.png)
+
+ relative order of the segments across sweep line +
This order is interesting because intersecting segments will have the same $y$-coordinate at least at one time: -
![intersection point having same y-coordinate](sweep_line_3.png)
+
+ intersection point having same y-coordinate +
We formulate key statements: diff --git a/src/geometry/lattice-points.md b/src/geometry/lattice-points.md index 8bd9db346..e3d6faf7e 100644 --- a/src/geometry/lattice-points.md +++ b/src/geometry/lattice-points.md @@ -38,7 +38,9 @@ We have two cases: This amount is the same as the number of points such that $0 < y \leq (k - \lfloor k \rfloor) \cdot x + (b - \lfloor b \rfloor)$. So we reduced our problem to $k'= k - \lfloor k \rfloor$, $b' = b - \lfloor b \rfloor$ and both $k'$ and $b'$ less than $1$ now. Here is a picture, we just summed up blue points and subtracted the blue linear function from the black one to reduce problem to smaller values for $k$ and $b$: -
![Subtracting floored linear function](lattice.png)
+
+ Subtracting floored linear function +
- $k < 1$ and $b < 1$. @@ -51,7 +53,9 @@ We have two cases: which returns us back to the case $k>1$. You can see new reference point $O'$ and axes $X'$ and $Y'$ in the picture below: -
![New reference and axes](mirror.png)
+
+ New reference and axes +
As you see, in new reference system linear function will have coefficient $\tfrac 1 k$ and its zero will be in the point $\lfloor k\cdot n + b \rfloor-(k\cdot n+b)$ which makes formula above correct. ## Complexity analysis diff --git a/src/geometry/manhattan-distance.md b/src/geometry/manhattan-distance.md index 2aeb746af..4c725626e 100644 --- a/src/geometry/manhattan-distance.md +++ b/src/geometry/manhattan-distance.md @@ -12,7 +12,9 @@ $$d(p,q) = |x_p - x_q| + |y_p - y_q|$$ Defined this way, the distance corresponds to the so-called [Manhattan (taxicab) geometry](https://en.wikipedia.org/wiki/Taxicab_geometry), in which the points are considered intersections in a well designed city, like Manhattan, where you can only move on the streets horizontally or vertically, as shown in the image below: -
![Manhattan Distance](https://upload.wikimedia.org/wikipedia/commons/thumb/0/08/Manhattan_distance.svg/220px-Manhattan_distance.svg.png)
+
+ Manhattan Distance +
This images show some of the smallest paths from one black point to the other, all of them with length $12$. @@ -77,7 +79,9 @@ Also, we may realize that $\alpha$ is a [spiral similarity](https://en.wikipedia Here's an image to help visualizing the transformation: -
![Chebyshev transformation](chebyshev-transformation.png)
+
+ Chebyshev transformation +
## Manhattan Minimum Spanning Tree diff --git a/src/geometry/minkowski.md b/src/geometry/minkowski.md index d3fd93d5d..f2661d867 100644 --- a/src/geometry/minkowski.md +++ b/src/geometry/minkowski.md @@ -41,7 +41,9 @@ We repeat the following steps while $i < |P|$ or $j < |Q|$. Here is a nice visualization, which may help you understand what is going on. -
![Visual](minkowski.gif)
+
+ Visual +
## Distance between two polygons One of the most common applications of Minkowski sum is computing the distance between two convex polygons (or simply checking whether they intersect). diff --git a/src/geometry/planar.md b/src/geometry/planar.md index 017399a7c..2cfc81282 100644 --- a/src/geometry/planar.md +++ b/src/geometry/planar.md @@ -53,7 +53,9 @@ It's quite clear that the complexity of the algorithm is $O(m \log m)$ because o At the first glance it may seem that finding faces of a disconnected graph is not much harder because we can run the same algorithm for each connected component. However, the components may be drawn in a nested way, forming **holes** (see the image below). In this case the inner face of some component becomes the outer face of some other components and has a complex disconnected border. Dealing with such cases is quite hard, one possible approach is to identify nested components with [point location](point-location.md) algorithms. -
![Planar graph with holes](planar_hole.png)
+
+ Planar graph with holes +
## Implementation The following implementation returns a vector of vertices for each face, outer face goes first. @@ -147,7 +149,9 @@ std::vector> find_faces(std::vector vertices, std::ve Sometimes you are not given a graph explicitly, but rather as a set of line segments on a plane, and the actual graph is formed by intersecting those segments, as shown in the picture below. In this case you have to build the graph manually. The easiest way to do so is as follows. Fix a segment and intersect it with all other segments. Then sort all intersection points together with the two endpoints of the segment lexicographically and add them to the graph as vertices. Also link each two adjacent vertices in lexicographical order by an edge. After doing this procedure for all edges we will obtain the graph. Of course, we should ensure that two equal intersection points will always correspond to the same vertex. The easiest way to do this is to store the points in a map by their coordinates, regarding points whose coordinates differ by a small number (say, less than $10^{-9}$) as equal. This algorithm works in $O(n^2 \log n)$. -
![Implicitly defined graph](planar_implicit.png)
+
+ Implicitly defined graph +
## Implementation ```{.cpp file=planar_implicit} diff --git a/src/geometry/point-location.md b/src/geometry/point-location.md index 10c0d3f51..c6d21b7f8 100644 --- a/src/geometry/point-location.md +++ b/src/geometry/point-location.md @@ -15,7 +15,9 @@ This problem may arise when you need to locate some points in a Voronoi diagram Firstly, for each query point $p\ (x_0, y_0)$ we want to find such an edge that if the point belongs to any edge, the point lies on the edge we found, otherwise this edge must intersect the line $x = x_0$ at some unique point $(x_0, y)$ where $y < y_0$ and this $y$ is maximum among all such edges. The following image shows both cases. -
![Image of Goal](point_location_goal.png)
+
+ Image of Goal +
We will solve this problem offline using the sweep line algorithm. Let's iterate over x-coordinates of query points and edges' endpoints in increasing order and keep a set of edges $s$. For each x-coordinate we will add some events beforehand. @@ -27,7 +29,9 @@ Finally, for each query point we will add one _get_ event for its x-coordinate. For each x-coordinate we will sort the events by their types in order (_vertical_, _get_, _remove_, _add_). The following image shows all events in sorted order for each x-coordinate. -
![Image of Events](point_location_events.png)
+
+ Image of Events +
We will keep two sets during the sweep-line process. A set $t$ for all non-vertical edges, and one set $vert$ especially for the vertical ones. diff --git a/src/geometry/tangents-to-two-circles.md b/src/geometry/tangents-to-two-circles.md index 4f4dfa4e9..546150a62 100644 --- a/src/geometry/tangents-to-two-circles.md +++ b/src/geometry/tangents-to-two-circles.md @@ -14,7 +14,9 @@ The described algorithm will also work in the case when one (or both) circles de ## The number of common tangents The number of common tangents to two circles can be **0,1,2,3,4** and **infinite**. Look at the images for different cases. -
!["Different cases of tangents common to two circles"](tangents-to-two-circles.png)
+
+ +
Here, we won't be considering **degenerate** cases, i.e *when the circles coincide (in this case they have infinitely many common tangents), or one circle lies inside the other (in this case they have no common tangents, or if the circles are tangent, there is one common tangent).* diff --git a/src/geometry/vertical_decomposition.md b/src/geometry/vertical_decomposition.md index 934db8044..96853994f 100644 --- a/src/geometry/vertical_decomposition.md +++ b/src/geometry/vertical_decomposition.md @@ -39,7 +39,9 @@ For simplicity we will show how to do this for an upper segment, the algorithm f Here is a graphic representation of the three cases. -
![Visual](triangle_union.png)
+
+ Visual +
Finally we should remark on processing all the additions of $1$ or $-1$ on all stripes in $[x_1, x_2]$. For each addition of $w$ on $[x_1, x_2]$ we can create events $(x_1, w),\ (x_2, -w)$ and process all these events with a sweep line. diff --git a/src/graph/2SAT.md b/src/graph/2SAT.md index b66316bbe..fbd1a6a35 100644 --- a/src/graph/2SAT.md +++ b/src/graph/2SAT.md @@ -39,7 +39,9 @@ b \Rightarrow a & \lnot b \Rightarrow \lnot a & b \Rightarrow \lnot a & c \Right You can see the implication graph in the following image: -
!["Implication Graph of 2-SAT example"](2SAT.png)
+
+ +
It is worth paying attention to the property of the implication graph: if there is an edge $a \Rightarrow b$, then there also is an edge $\lnot b \Rightarrow \lnot a$. @@ -59,7 +61,9 @@ The following image shows all strongly connected components for the example. As we can check easily, neither of the four components contain a vertex $x$ and its negation $\lnot x$, therefore the example has a solution. We will learn in the next paragraphs how to compute a valid assignment, but just for demonstration purposes the solution $a = \text{false}$, $b = \text{false}$, $c = \text{false}$ is given. -
!["Strongly Connected Components of the 2-SAT example"](2SAT_SCC.png)
+
+ +
Now we construct the algorithm for finding the solution of the 2-SAT problem on the assumption that the solution exists. diff --git a/src/graph/edmonds_karp.md b/src/graph/edmonds_karp.md index ef844f970..a86a475f9 100644 --- a/src/graph/edmonds_karp.md +++ b/src/graph/edmonds_karp.md @@ -43,7 +43,9 @@ The source $s$ is origin of all the water, and the water can only drain in the s The following image shows a flow network. The first value of each edge represents the flow, which is initially 0, and the second value represents the capacity. -
![Flow network](Flow1.png)
+
+ Flow network +
The value of the flow of a network is the sum of all the flows that get produced in the source $s$, or equivalently to the sum of all the flows that are consumed by the sink $t$. A **maximal flow** is a flow with the maximal possible value. @@ -53,7 +55,9 @@ In the visualization with water pipes, the problem can be formulated in the foll how much water can we push through the pipes from the source to the sink? The following image shows the maximal flow in the flow network. -
![Maximal flow](Flow9.png)
+
+ Maximal flow +
## Ford-Fulkerson method @@ -79,7 +83,9 @@ we update $f((u, v)) ~\text{+=}~ C$ and $f((v, u)) ~\text{-=}~ C$ for every edge Here is an example to demonstrate the method. We use the same flow network as above. Initially we start with a flow of 0. -
![Flow network](Flow1.png)
+
+ Flow network +
We can find the path $s - A - B - t$ with the residual capacities 7, 5, and 8. Their minimum is 5, therefore we can increase the flow along this path by 5. @@ -200,7 +206,9 @@ It says that the capacity of the maximum flow has to be equal to the capacity of In the following image, you can see the minimum cut of the flow network we used earlier. It shows that the capacity of the cut $\{s, A, D\}$ and $\{B, C, t\}$ is $5 + 3 + 2 = 10$, which is equal to the maximum flow that we found. Other cuts will have a bigger capacity, like the capacity between $\{s, A\}$ and $\{B, C, D, t\}$ is $4 + 3 + 5 = 12$. -
![Minimum cut](Cut.png)
+
+ Minimum cut +
A minimum cut can be found after performing a maximum flow computation using the Ford-Fulkerson method. One possible minimum cut is the following: diff --git a/src/graph/hld.md b/src/graph/hld.md index bd56798fd..36caf852e 100644 --- a/src/graph/hld.md +++ b/src/graph/hld.md @@ -53,7 +53,9 @@ Since we can move from one heavy path to another only through a light edge (each The following image illustrates the decomposition of a sample tree. The heavy edges are thicker than the light edges. The heavy paths are marked by dotted boundaries. -
![Image of HLD](hld.png)
+
+ Image of HLD +
## Sample problems diff --git a/src/graph/lca.md b/src/graph/lca.md index db36c18a0..f577d4b2f 100644 --- a/src/graph/lca.md +++ b/src/graph/lca.md @@ -30,7 +30,9 @@ So the $\text{LCA}(v_1, v_2)$ can be uniquely determined by finding the vertex w Let's illustrate this idea. Consider the following graph and the Euler tour with the corresponding heights: -
![LCA_Euler_Tour](LCA_Euler.png)
+
+ LCA_Euler_Tour +
$$\begin{array}{|l|c|c|c|c|c|c|c|c|c|c|c|c|c|} \hline diff --git a/src/graph/lca_farachcoltonbender.md b/src/graph/lca_farachcoltonbender.md index f67365511..506509359 100644 --- a/src/graph/lca_farachcoltonbender.md +++ b/src/graph/lca_farachcoltonbender.md @@ -22,7 +22,9 @@ The LCA of two nodes $u$ and $v$ is the node between the occurrences of $u$ and In the following picture you can see a possible Euler-Tour of a graph and in the list below you can see the visited nodes and their heights. -
![LCA_Euler_Tour](LCA_Euler.png)
+
+ LCA_Euler_Tour +
$$\begin{array}{|l|c|c|c|c|c|c|c|c|c|c|c|c|c|} \hline diff --git a/src/graph/rmq_linear.md b/src/graph/rmq_linear.md index 9ce17f341..ab0ae216a 100644 --- a/src/graph/rmq_linear.md +++ b/src/graph/rmq_linear.md @@ -24,7 +24,9 @@ The array `A` will be partitioned into 3 parts: the prefix of the array up to th The root of the tree will be a node corresponding to the minimum element of the array `A`, the left subtree will be the Cartesian tree of the prefix, and the right subtree will be a Cartesian tree of the suffix. In the following image you can see one array of length 10 and the corresponding Cartesian tree. -
![Image of Cartesian Tree](CartesianTree.png)
+
+ Image of Cartesian Tree +
The range minimum query `[l, r]` is equivalent to the lowest common ancestor query `[l', r']`, where `l'` is the node corresponding to the element `A[l]` and `r'` the node corresponding to the element `A[r]`. Indeed the node corresponding to the smallest element in the range has to be an ancestor of all nodes in the range, therefor also from `l'` and `r'`. @@ -33,7 +35,9 @@ And is also has to be the lowest ancestor, because otherwise `l'` and `r'` would In the following image you can see the LCA queries for the RMQ queries `[1, 3]` and `[5, 9]`. In the first query the LCA of the nodes `A[1]` and `A[3]` is the node corresponding to `A[2]` which has the value 2, and in the second query the LCA of `A[5]` and `A[9]` is the node corresponding to `A[8]` which has the value 3. -
![LCA queries in the Cartesian Tree](CartesianTreeLCA.png)
+
+ LCA queries in the Cartesian Tree +
Such a tree can be built in $O(N)$ time and the Farach-Colton and Benders algorithm can preprocess the tree in $O(N)$ and find the LCA in $O(1)$. diff --git a/src/graph/topological-sort.md b/src/graph/topological-sort.md index acfad5331..909e40652 100644 --- a/src/graph/topological-sort.md +++ b/src/graph/topological-sort.md @@ -20,9 +20,9 @@ Here is one given graph together with its topological order: Topological order can be **non-unique** (for example, if there exist three vertices $a$, $b$, $c$ for which there exist paths from $a$ to $b$ and from $a$ to $c$ but not paths from $b$ to $c$ or from $c$ to $b$). The example graph also has multiple topological orders, a second topological order is the following: -
-![second topological order](topological_3.png) -
+
+ second topological order +
A Topological order may **not exist** at all. It only exists, if the directed graph contains no cycles. diff --git a/src/others/stern_brocot_tree_farey_sequences.md b/src/others/stern_brocot_tree_farey_sequences.md index bb9dfd725..f725de53b 100644 --- a/src/others/stern_brocot_tree_farey_sequences.md +++ b/src/others/stern_brocot_tree_farey_sequences.md @@ -34,7 +34,9 @@ Continuing this process to infinity this covers *all* positive fractions. Additi Before proving these properties, let us actually show a visualization of the Stern-Brocot tree, rather than the list representation. Every fraction in the tree has two children. Each child is the mediant of the closest ancestor on the left and closest ancestor to the right. -
![Stern-Brocot tree](https://upload.wikimedia.org/wikipedia/commons/thumb/3/37/SternBrocotTree.svg/1024px-SternBrocotTree.svg.png)
+
+ Stern-Brocot tree +
## Proofs diff --git a/src/others/tortoise_and_hare.md b/src/others/tortoise_and_hare.md index 9d2ba0a96..783185542 100644 --- a/src/others/tortoise_and_hare.md +++ b/src/others/tortoise_and_hare.md @@ -7,7 +7,9 @@ tags: Given a linked list where the starting point of that linked list is denoted by **head**, and there may or may not be a cycle present. For instance: -
!["Linked list with cycle"](tortoise_hare_algo.png)
+
+ +
Here we need to find out the point **C**, i.e the starting point of the cycle. @@ -27,7 +29,9 @@ So, it involved two steps: 6. If they point to any same node at any point of their journey, it would indicate that the cycle indeed exists in the linked list. 7. If we get null, it would indicate that the linked list has no cycle. -
!["Found cycle"](tortoise_hare_cycle_found.png)
+
+ +
Now, that we have figured out that there is a cycle present in the linked list, for the next step we need to find out the starting point of cycle, i.e., **C**. ### Step 2: Starting point of the cycle @@ -81,7 +85,9 @@ When the slow pointer has moved $k \cdot L$ steps, and the fast pointer has cove Lets try to calculate the distance covered by both of the pointers till they point they met within the cycle. -
!["Proof"](tortoise_hare_proof.png)
+
+ +
$slowDist = a + xL + b$ , $x\ge0$ From a1adc156765822f6f53c5d2cb432ae0818c00882 Mon Sep 17 00:00:00 2001 From: Michael Hayter Date: Tue, 15 Apr 2025 20:31:59 -0400 Subject: [PATCH 12/26] Update k-th.md #1215 basics solved. --- src/sequences/k-th.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sequences/k-th.md b/src/sequences/k-th.md index 02b0c0ec2..0f588c5cb 100644 --- a/src/sequences/k-th.md +++ b/src/sequences/k-th.md @@ -68,7 +68,7 @@ T order_statistics (std::vector a, unsigned n, unsigned k) ## Notes * The randomized algorithm above is named [quickselect](https://en.wikipedia.org/wiki/Quickselect). You should do random shuffle on $A$ before calling it or use a random element as a barrier for it to run properly. There are also deterministic algorithms that solve the specified problem in linear time, such as [median of medians](https://en.wikipedia.org/wiki/Median_of_medians). -* A deterministic linear solution is implemented in C++ standard library as [std::nth_element](https://en.cppreference.com/w/cpp/algorithm/nth_element). +* [std::nth_element](https://en.cppreference.com/w/cpp/algorithm/nth_element) solves this in C++ but gcc's implementation runs in worst case $O(n \log n )$ time. * Finding $K$ smallest elements can be reduced to finding $K$-th element with a linear overhead, as they're exactly the elements that are smaller than $K$-th. ## Practice Problems From a65db14a555810306ec6af024bc9f0afd2e0a60b Mon Sep 17 00:00:00 2001 From: Michael Hayter Date: Tue, 15 Apr 2025 20:46:24 -0400 Subject: [PATCH 13/26] Update finding-negative-cycle-in-graph.md Resolves 1419. --- src/graph/finding-negative-cycle-in-graph.md | 21 +++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/graph/finding-negative-cycle-in-graph.md b/src/graph/finding-negative-cycle-in-graph.md index 7589c09db..a9b87c7f9 100644 --- a/src/graph/finding-negative-cycle-in-graph.md +++ b/src/graph/finding-negative-cycle-in-graph.md @@ -30,35 +30,33 @@ Do $N$ iterations of Bellman-Ford algorithm. If there were no changes on the las struct Edge { int a, b, cost; }; - -int n, m; + +int n; vector edges; const int INF = 1000000000; - + void solve() { - vector d(n, INF); + vector d(n, 0); vector p(n, -1); int x; - - d[0] = 0; - + for (int i = 0; i < n; ++i) { x = -1; for (Edge e : edges) { - if (d[e.a] < INF && d[e.a] + e.cost < d[e.b]) { + if (d[e.a] + e.cost < d[e.b]) { d[e.b] = max(-INF, d[e.a] + e.cost); p[e.b] = e.a; x = e.b; } } } - + if (x == -1) { cout << "No negative cycle found."; } else { for (int i = 0; i < n; ++i) x = p[x]; - + vector cycle; for (int v = x;; v = p[v]) { cycle.push_back(v); @@ -66,14 +64,13 @@ void solve() { break; } reverse(cycle.begin(), cycle.end()); - + cout << "Negative cycle: "; for (int v : cycle) cout << v << ' '; cout << endl; } } - ``` ## Using Floyd-Warshall algorithm From b21558de0edde5d4edb1954b565fc6e10b24f5ee Mon Sep 17 00:00:00 2001 From: Mrityunjai Singh Date: Wed, 16 Apr 2025 01:32:22 -0700 Subject: [PATCH 14/26] aho corasick text change --- src/string/aho_corasick.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/string/aho_corasick.md b/src/string/aho_corasick.md index d56ed5c1c..df0998713 100644 --- a/src/string/aho_corasick.md +++ b/src/string/aho_corasick.md @@ -209,7 +209,7 @@ Assume that at the moment we stand in a vertex $v$ and consider a character $c$. 1. $go[v][c] = -1$. In this case, we may assign $go[v][c] = go[u][c]$, which is already known by the induction hypothesis; 2. $go[v][c] = w \neq -1$. In this case, we may assign $link[w] = go[u][c]$. -In this way, we spend $O(1)$ time per each pair of a vertex and a character, making the running time $O(mk)$. The major overhead here is that we copy a lot of transitions from $u$ in the first case, while the transitions of the second case form the trie and sum up to $m$ over all vertices. To avoid the copying of $go[u][c]$, we may use a persistent array data structure, using which we initially copy $go[u]$ into $go[v]$ and then only update values for characters in which the transition would differ. This leads to the $O(m \log k)$ algorithm. +In this way, we spend $O(1)$ time per each pair of a vertex and a character, making the running time $O(mk)$. The major overhead here is that we copy a lot of transitions from $u$ in the first case, while the transitions of the second case form the trie and sum up to $m$ over all vertices. To avoid the copying of $go[u][c]$, we may use a persistent array data structure, using which we initially copy $go[u]$ into $go[v]$ and then only update values for characters in which the transition would differ. This leads to the $O(n \log k)$ algorithm. ## Applications From 4cdc4df2c108f77037a5e0b5e508de7548ec7c8d Mon Sep 17 00:00:00 2001 From: jxu <7989982+jxu@users.noreply.github.com> Date: Thu, 17 Apr 2025 13:52:46 -0400 Subject: [PATCH 15/26] Fibonacci: better motivation for matrix form --- src/algebra/fibonacci-numbers.md | 43 ++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/src/algebra/fibonacci-numbers.md b/src/algebra/fibonacci-numbers.md index 22403419a..adc409d3e 100644 --- a/src/algebra/fibonacci-numbers.md +++ b/src/algebra/fibonacci-numbers.md @@ -116,9 +116,48 @@ In this way, we obtain a linear solution, $O(n)$ time, saving all the values pri ### Matrix form -It is easy to prove the following relation: +To go from $(F_n, F_{n-1})$ to $(F_{n+1}, F_n)$, we can express the linear recurrence as a 2x2 matrix multiplication: -$$\begin{pmatrix} 1 & 1 \cr 1 & 0 \cr\end{pmatrix} ^ n = \begin{pmatrix} F_{n+1} & F_{n} \cr F_{n} & F_{n-1} \cr\end{pmatrix}$$ +$$ +\begin{pmatrix} +1 & 1 \\ +1 & 0 +\end{pmatrix} +\begin{pmatrix} +F_n \\ +F_{n-1} +\end{pmatrix} += +\begin{pmatrix} +F_n + F_{n-1} \\ +F_{n} +\end{pmatrix} += +\begin{pmatrix} +F_{n+1} \\ +F_{n} +\end{pmatrix} +$$ + +This lets us treat iterating the recurrence as repeated matrix multiplication, which has nice properties. In particular, + +$$ +\begin{pmatrix} +1 & 1 \\ +1 & 0 +\end{pmatrix}^n +\begin{pmatrix} +F_1 \\ +F_0 +\end{pmatrix} += +\begin{pmatrix} +F_{n+1} \\ +F_{n} +\end{pmatrix} +$$ + +where $F_1 = 1, F_0 = 0$. Thus, in order to find $F_n$ in $O(log n)$ time, we must raise the matrix to n. (See [Binary exponentiation](binary-exp.md)) From 7520d3e539694f053614ed9e8e1fd3bf980338c7 Mon Sep 17 00:00:00 2001 From: jxu <7989982+jxu@users.noreply.github.com> Date: Thu, 17 Apr 2025 14:03:29 -0400 Subject: [PATCH 16/26] Fibonacci: Cassini's identity proof sketches --- src/algebra/fibonacci-numbers.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/algebra/fibonacci-numbers.md b/src/algebra/fibonacci-numbers.md index 22403419a..7c805a629 100644 --- a/src/algebra/fibonacci-numbers.md +++ b/src/algebra/fibonacci-numbers.md @@ -22,6 +22,8 @@ Fibonacci numbers possess a lot of interesting properties. Here are a few of the $$F_{n-1} F_{n+1} - F_n^2 = (-1)^n$$ +This can be proved by induction. A one-line proof due to Knuth comes from taking the determinant of the 2x2 matrix form below. + * The "addition" rule: $$F_{n+k} = F_k F_{n+1} + F_{k-1} F_n$$ From 375ca6b9368247b01a0722a8d184418c3c5bebe1 Mon Sep 17 00:00:00 2001 From: Oleksandr Kulkov Date: Fri, 18 Apr 2025 20:42:14 +0200 Subject: [PATCH 17/26] log n -> \log n --- src/algebra/fibonacci-numbers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/algebra/fibonacci-numbers.md b/src/algebra/fibonacci-numbers.md index adc409d3e..e63e52081 100644 --- a/src/algebra/fibonacci-numbers.md +++ b/src/algebra/fibonacci-numbers.md @@ -159,7 +159,7 @@ $$ where $F_1 = 1, F_0 = 0$. -Thus, in order to find $F_n$ in $O(log n)$ time, we must raise the matrix to n. (See [Binary exponentiation](binary-exp.md)) +Thus, in order to find $F_n$ in $O(\log n)$ time, we must raise the matrix to n. (See [Binary exponentiation](binary-exp.md)) ```{.cpp file=fibonacci_matrix} struct matrix { From 8c5bc69c37613441b49493974c9bfa7b04aa7deb Mon Sep 17 00:00:00 2001 From: Oleksandr Kulkov Date: Fri, 18 Apr 2025 23:47:45 +0200 Subject: [PATCH 18/26] m -> n in Aho-Corasick --- src/string/aho_corasick.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/string/aho_corasick.md b/src/string/aho_corasick.md index df0998713..36ec40c7d 100644 --- a/src/string/aho_corasick.md +++ b/src/string/aho_corasick.md @@ -209,7 +209,7 @@ Assume that at the moment we stand in a vertex $v$ and consider a character $c$. 1. $go[v][c] = -1$. In this case, we may assign $go[v][c] = go[u][c]$, which is already known by the induction hypothesis; 2. $go[v][c] = w \neq -1$. In this case, we may assign $link[w] = go[u][c]$. -In this way, we spend $O(1)$ time per each pair of a vertex and a character, making the running time $O(mk)$. The major overhead here is that we copy a lot of transitions from $u$ in the first case, while the transitions of the second case form the trie and sum up to $m$ over all vertices. To avoid the copying of $go[u][c]$, we may use a persistent array data structure, using which we initially copy $go[u]$ into $go[v]$ and then only update values for characters in which the transition would differ. This leads to the $O(n \log k)$ algorithm. +In this way, we spend $O(1)$ time per each pair of a vertex and a character, making the running time $O(nk)$. The major overhead here is that we copy a lot of transitions from $u$ in the first case, while the transitions of the second case form the trie and sum up to $n$ over all vertices. To avoid the copying of $go[u][c]$, we may use a persistent array data structure, using which we initially copy $go[u]$ into $go[v]$ and then only update values for characters in which the transition would differ. This leads to the $O(n \log k)$ algorithm. ## Applications From 115cc22af2ba00e76f123f2a382afc44ff9710d4 Mon Sep 17 00:00:00 2001 From: jxu <7989982+jxu@users.noreply.github.com> Date: Fri, 18 Apr 2025 23:11:59 -0400 Subject: [PATCH 19/26] Fibonacci: restore matrix power form Maybe a dotted line would show the matrix [[F2, F1],[F1,F0]] can be viewed as two column vectors. Using only the matrix power saves one matrix-vector multiply. --- src/algebra/fibonacci-numbers.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/algebra/fibonacci-numbers.md b/src/algebra/fibonacci-numbers.md index e63e52081..e1acfdcb0 100644 --- a/src/algebra/fibonacci-numbers.md +++ b/src/algebra/fibonacci-numbers.md @@ -157,7 +157,19 @@ F_{n} \end{pmatrix} $$ -where $F_1 = 1, F_0 = 0$. +where $F_1 = 1, F_0 = 0$. +In fact, since +$$ +\begin{pmatrix} 1 & 1 \\ 1 & 0 \end{pmatrix} += \begin{pmatrix} F_2 & F_1 \\ F_1 & F_0 \end{pmatrix} +$$ + +we can use the matrix directly: + +$$ +\begin{pmatrix} 1 & 1 \\ 1 & 0 \end{pmatrix}^n += \begin{pmatrix} F_{n+1} & F_n \\ F_n & F_{n-1} \end{pmatrix} +$$ Thus, in order to find $F_n$ in $O(\log n)$ time, we must raise the matrix to n. (See [Binary exponentiation](binary-exp.md)) From 59c31747c57735cfe83639f9a1dac64bb800dd78 Mon Sep 17 00:00:00 2001 From: Oleksandr Kulkov Date: Sat, 19 Apr 2025 09:56:55 +0200 Subject: [PATCH 20/26] Update src/algebra/fibonacci-numbers.md --- src/algebra/fibonacci-numbers.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/algebra/fibonacci-numbers.md b/src/algebra/fibonacci-numbers.md index e1acfdcb0..dd8e1bd1a 100644 --- a/src/algebra/fibonacci-numbers.md +++ b/src/algebra/fibonacci-numbers.md @@ -159,6 +159,7 @@ $$ where $F_1 = 1, F_0 = 0$. In fact, since + $$ \begin{pmatrix} 1 & 1 \\ 1 & 0 \end{pmatrix} = \begin{pmatrix} F_2 & F_1 \\ F_1 & F_0 \end{pmatrix} From 06d6a835e50e0d66de3c817a322eb292e69d23d5 Mon Sep 17 00:00:00 2001 From: Michael Hayter Date: Mon, 21 Apr 2025 21:11:00 -0400 Subject: [PATCH 21/26] Update fibonacci-numbers.md Try indentation. --- src/algebra/fibonacci-numbers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/algebra/fibonacci-numbers.md b/src/algebra/fibonacci-numbers.md index 7c805a629..176a73600 100644 --- a/src/algebra/fibonacci-numbers.md +++ b/src/algebra/fibonacci-numbers.md @@ -22,7 +22,7 @@ Fibonacci numbers possess a lot of interesting properties. Here are a few of the $$F_{n-1} F_{n+1} - F_n^2 = (-1)^n$$ -This can be proved by induction. A one-line proof due to Knuth comes from taking the determinant of the 2x2 matrix form below. +>This can be proved by induction. A one-line proof by Knuth comes from taking the determinant of the 2x2 matrix form below. * The "addition" rule: From 3de210a3040f8b2dab5cf64286f3c28e02747291 Mon Sep 17 00:00:00 2001 From: Michael Hayter Date: Mon, 21 Apr 2025 22:11:52 -0400 Subject: [PATCH 22/26] updated script to account for when text follows center tag --- src/geometry/manhattan-distance.md | 28 ++++++++++++++++------------ src/graph/edmonds_karp.md | 20 ++++++++++++++++---- src/graph/mst_prim.md | 5 ++++- src/graph/second_best_mst.md | 6 ++++-- src/graph/topological-sort.md | 8 ++++---- 5 files changed, 44 insertions(+), 23 deletions(-) diff --git a/src/geometry/manhattan-distance.md b/src/geometry/manhattan-distance.md index 4c725626e..87514868a 100644 --- a/src/geometry/manhattan-distance.md +++ b/src/geometry/manhattan-distance.md @@ -88,17 +88,19 @@ Here's an image to help visualizing the transformation: The Manhattan MST problem consists of, given some points in the plane, find the edges that connect all the points and have a minimum total sum of weights. The weight of an edge that connects two points is their Manhattan distance. For simplicity, we assume that all points have different locations. Here we show a way of finding the MST in $O(n \log{n})$ by finding for each point its nearest neighbor in each octant, as represented by the image below. This will give us $O(n)$ candidate edges, which, as we show below, will guarantee that they contain the MST. The final step is then using some standard MST, for example, [Kruskal algorithm using disjoint set union](https://cp-algorithms.com/graph/mst_kruskal_with_dsu.html). -
![8 octants picture](manhattan-mst-octants.png) - -*The 8 octants relative to a point S*
+
+ 8 octants picture + *The 8 octants relative to a point S* +
The algorithm shown here was first presented in a paper from [H. Zhou, N. Shenoy, and W. Nichollos (2002)](https://ieeexplore.ieee.org/document/913303). There is also another know algorithm that uses a Divide and conquer approach by [J. Stolfi](https://www.academia.edu/15667173/On_computing_all_north_east_nearest_neighbors_in_the_L1_metric), which is also very interesting and only differ in the way they find the nearest neighbor in each octant. They both have the same complexity, but the one presented here is easier to implement and has a lower constant factor. First, let's understand why it is enough to consider only the nearest neighbor in each octant. The idea is to show that for a point $s$ and any two other points $p$ and $q$ in the same octant, $d(p, q) < \max(d(s, p), d(s, q))$. This is important, because it shows that if there was a MST where $s$ is connected to both $p$ and $q$, we could erase one of these edges and add the edge $(p,q)$, which would decrease the total cost. To prove this, we assume without loss of generality that $p$ and $q$ are in the octanct $R_1$, which is defined by: $x_s \leq x$ and $x_s - y_s > x - y$, and then do some casework. The image below give some intuition on why this is true. -
![unique nearest neighbor](manhattan-mst-uniqueness.png) - -*Intuitively, the limitation of the octant makes it impossible that $p$ and $q$ are both closer to $s$ than to each other*
+
+ unique nearest neighbor + *Intuitively, the limitation of the octant makes it impossible that $p$ and $q$ are both closer to $s$ than to each other* +
Therefore, the main question is how to find the nearest neighbor in each octant for every single of the $n$ points. @@ -109,13 +111,15 @@ For simplicity we focus on the NNE octant ($R_1$ in the image above). All other We will use a sweep-line approach. We process the points from south-west to north-east, that is, by non-decreasing $x + y$. We also keep a set of points which don't have their nearest neighbor yet, which we call "active set". We add the images below to help visualize the algorithm. -
![manhattan-mst-sweep](manhattan-mst-sweep-line-1.png) - -*In black with an arrow you can see the direction of the line-sweep. All the points below this lines are in the active set, and the points above are still not processed. In green we see the points which are in the octant of the processed point. In red the points that are not in the searched octant.*
- -
![manhattan-mst-sweep](manhattan-mst-sweep-line-2.png) +
+ manhattan-mst-sweep + *In black with an arrow you can see the direction of the line-sweep. All the points below this lines are in the active set, and the points above are still not processed. In green we see the points which are in the octant of the processed point. In red the points that are not in the searched octant.* +
-*In this image we see the active set after processing the point $p$. Note that the $2$ green points of the previous image had $p$ in its north-north-east octant and are not in the active set anymore, because they already found their nearest neighbor.*
+
+ manhattan-mst-sweep + *In this image we see the active set after processing the point $p$. Note that the $2$ green points of the previous image had $p$ in its north-north-east octant and are not in the active set anymore, because they already found their nearest neighbor.* +
When we add a new point point $p$, for every point $s$ that has it in its octant we can safely assign $p$ as the nearest neighbor. This is true because their distance is $d(p,s) = |x_p - x_s| + |y_p - y_s| = (x_p + y_p) - (x_s + y_s)$, because $p$ is in the north-north-east octant. As all the next points will not have a smaller value of $x + y$ because of the sorting step, $p$ is guaranteed to have the smaller distance. We can then remove all such points from the active set, and finally add $p$ to the active set. diff --git a/src/graph/edmonds_karp.md b/src/graph/edmonds_karp.md index a86a475f9..c1f394dce 100644 --- a/src/graph/edmonds_karp.md +++ b/src/graph/edmonds_karp.md @@ -90,14 +90,23 @@ Initially we start with a flow of 0. We can find the path $s - A - B - t$ with the residual capacities 7, 5, and 8. Their minimum is 5, therefore we can increase the flow along this path by 5. This gives a flow of 5 for the network. -
![First path](Flow2.png) ![Network after first path](Flow3.png)
+
+ First path + ![Network after first path](Flow3.png) +
Again we look for an augmenting path, this time we find $s - D - A - C - t$ with the residual capacities 4, 3, 3, and 5. Therefore we can increase the flow by 3 and we get a flow of 8 for the network. -
![Second path](Flow4.png) ![Network after second path](Flow5.png)
+
+ Second path + ![Network after second path](Flow5.png) +
This time we find the path $s - D - C - B - t$ with the residual capacities 1, 2, 3, and 3, and hence, we increase the flow by 1. -
![Third path](Flow6.png) ![Network after third path](Flow7.png)
+
+ Third path + ![Network after third path](Flow7.png) +
This time we find the augmenting path $s - A - D - C - t$ with the residual capacities 2, 3, 1, and 2. We can increase the flow by 1. @@ -107,7 +116,10 @@ In the original flow network, we are not allowed to send any flow from $A$ to $D But because we already have a flow of 3 from $D$ to $A$, this is possible. The intuition of it is the following: Instead of sending a flow of 3 from $D$ to $A$, we only send 2 and compensate this by sending an additional flow of 1 from $s$ to $A$, which allows us to send an additional flow of 1 along the path $D - C - t$. -
![Fourth path](Flow8.png) ![Network after fourth path](Flow9.png)
+
+ Fourth path + ![Network after fourth path](Flow9.png) +
Now, it is impossible to find an augmenting path between $s$ and $t$, therefore this flow of $10$ is the maximal possible. We have found the maximal flow. diff --git a/src/graph/mst_prim.md b/src/graph/mst_prim.md index d8c3789db..9f7eb48c8 100644 --- a/src/graph/mst_prim.md +++ b/src/graph/mst_prim.md @@ -13,7 +13,10 @@ The spanning tree with the least weight is called a minimum spanning tree. In the left image you can see a weighted undirected graph, and in the right image you can see the corresponding minimum spanning tree. -
![Random graph](MST_before.png) ![MST of this graph](MST_after.png)
+
+ Random graph + ![MST of this graph](MST_after.png) +
It is easy to see that any spanning tree will necessarily contain $n-1$ edges. diff --git a/src/graph/second_best_mst.md b/src/graph/second_best_mst.md index 5e9c82246..075f0dd39 100644 --- a/src/graph/second_best_mst.md +++ b/src/graph/second_best_mst.md @@ -53,10 +53,12 @@ The final time complexity of this approach is $O(E \log V)$. For example: -
![MST](second_best_mst_1.png) ![Second best MST](second_best_mst_2.png)
+
+ MST + ![Second best MST](second_best_mst_2.png)
*In the image left is the MST and right is the second best MST.* -
+ In the given graph suppose we root the MST at the blue vertex on the top, and then run our algorithm by start picking the edges not in MST. diff --git a/src/graph/topological-sort.md b/src/graph/topological-sort.md index 909e40652..262189a42 100644 --- a/src/graph/topological-sort.md +++ b/src/graph/topological-sort.md @@ -13,10 +13,10 @@ In other words, you want to find a permutation of the vertices (**topological or Here is one given graph together with its topological order: -
-![example directed graph](topological_1.png) -![one topological order](topological_2.png) -
+
+ example directed graph + ![one topological order](topological_2.png) +
Topological order can be **non-unique** (for example, if there exist three vertices $a$, $b$, $c$ for which there exist paths from $a$ to $b$ and from $a$ to $c$ but not paths from $b$ to $c$ or from $c$ to $b$). The example graph also has multiple topological orders, a second topological order is the following: From 9f5502ad2af0b6320be0ae8b7e27cf32c5b2d6bc Mon Sep 17 00:00:00 2001 From: Michael Hayter Date: Mon, 21 Apr 2025 22:22:24 -0400 Subject: [PATCH 23/26] change 2 images rather than image then text --- src/graph/mst_prim.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/graph/mst_prim.md b/src/graph/mst_prim.md index 9f7eb48c8..21649f28d 100644 --- a/src/graph/mst_prim.md +++ b/src/graph/mst_prim.md @@ -15,7 +15,7 @@ In the left image you can see a weighted undirected graph, and in the right imag
Random graph - ![MST of this graph](MST_after.png) + MST of this graph
It is easy to see that any spanning tree will necessarily contain $n-1$ edges. From fc8d5dc718a348a91a3d86e35ea8c64e8df47f00 Mon Sep 17 00:00:00 2001 From: Michael Hayter Date: Mon, 21 Apr 2025 22:26:46 -0400 Subject: [PATCH 24/26] formatted multiple images in center tag --- src/graph/second_best_mst.md | 3 ++- src/graph/topological-sort.md | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/graph/second_best_mst.md b/src/graph/second_best_mst.md index 075f0dd39..3a68ee34a 100644 --- a/src/graph/second_best_mst.md +++ b/src/graph/second_best_mst.md @@ -55,7 +55,8 @@ For example:
MST - ![Second best MST](second_best_mst_2.png)
+ Second best MST +
*In the image left is the MST and right is the second best MST.*
diff --git a/src/graph/topological-sort.md b/src/graph/topological-sort.md index 262189a42..c522039bc 100644 --- a/src/graph/topological-sort.md +++ b/src/graph/topological-sort.md @@ -15,7 +15,7 @@ Here is one given graph together with its topological order:
example directed graph - ![one topological order](topological_2.png) + one topological order
Topological order can be **non-unique** (for example, if there exist three vertices $a$, $b$, $c$ for which there exist paths from $a$ to $b$ and from $a$ to $c$ but not paths from $b$ to $c$ or from $c$ to $b$). From f46a230f922fa344a7c5cce28292835ec315a454 Mon Sep 17 00:00:00 2001 From: Michael Hayter Date: Mon, 21 Apr 2025 22:45:52 -0400 Subject: [PATCH 25/26] double images not working in edmonds karp --- src/graph/edmonds_karp.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/graph/edmonds_karp.md b/src/graph/edmonds_karp.md index c1f394dce..bab54772f 100644 --- a/src/graph/edmonds_karp.md +++ b/src/graph/edmonds_karp.md @@ -92,20 +92,20 @@ Their minimum is 5, therefore we can increase the flow along this path by 5. This gives a flow of 5 for the network.
First path - ![Network after first path](Flow3.png) + Network after first path
Again we look for an augmenting path, this time we find $s - D - A - C - t$ with the residual capacities 4, 3, 3, and 5. Therefore we can increase the flow by 3 and we get a flow of 8 for the network.
Second path - ![Network after second path](Flow5.png) + Network after second path
This time we find the path $s - D - C - B - t$ with the residual capacities 1, 2, 3, and 3, and hence, we increase the flow by 1.
Third path - ![Network after third path](Flow7.png) + Network after third path
This time we find the augmenting path $s - A - D - C - t$ with the residual capacities 2, 3, 1, and 2. @@ -118,7 +118,7 @@ The intuition of it is the following: Instead of sending a flow of 3 from $D$ to $A$, we only send 2 and compensate this by sending an additional flow of 1 from $s$ to $A$, which allows us to send an additional flow of 1 along the path $D - C - t$.
Fourth path - ![Network after fourth path](Flow9.png) + Network after fourth path
Now, it is impossible to find an augmenting path between $s$ and $t$, therefore this flow of $10$ is the maximal possible. From 46e4efa5180bfa059694dec086269806832105a7 Mon Sep 17 00:00:00 2001 From: Shashank Sahu <52148284+bit-shashank@users.noreply.github.com> Date: Sat, 3 May 2025 00:07:17 +0530 Subject: [PATCH 26/26] Typo fix in graph/fixed_length_paths.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaced It is obvious that the constructed adjacency matrix if the answer to the problem for the case   $k = 1$ . It contains the number of paths of length   $1$  between each pair of vertices. To It is obvious that the constructed adjacency matrix is the answer to the problem for the case   $k = 1$ . It contains the number of paths of length   $1$  between each pair of vertices. --- src/graph/fixed_length_paths.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/graph/fixed_length_paths.md b/src/graph/fixed_length_paths.md index e5ecf76bc..9e1c1efad 100644 --- a/src/graph/fixed_length_paths.md +++ b/src/graph/fixed_length_paths.md @@ -21,7 +21,7 @@ The following algorithm works also in the case of multiple edges: if some pair of vertices $(i, j)$ is connected with $m$ edges, then we can record this in the adjacency matrix by setting $G[i][j] = m$. Also the algorithm works if the graph contains loops (a loop is an edge that connect a vertex with itself). -It is obvious that the constructed adjacency matrix if the answer to the problem for the case $k = 1$. +It is obvious that the constructed adjacency matrix is the answer to the problem for the case $k = 1$. It contains the number of paths of length $1$ between each pair of vertices. We will build the solution iteratively: