From 4bbd576ec0f4c26a6710ecae6050e2522c84616c Mon Sep 17 00:00:00 2001 From: Wiktor Kuchta Date: Fri, 1 Feb 2019 21:50:12 +0100 Subject: [PATCH 1/8] Fix typo in Edmonds-Karp --- src/graph/edmonds_karp.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/graph/edmonds_karp.md b/src/graph/edmonds_karp.md index 8c2c206be..07b45d8ba 100644 --- a/src/graph/edmonds_karp.md +++ b/src/graph/edmonds_karp.md @@ -36,7 +36,7 @@ The following image show 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](&imgroot&/Flow1.png)
-We value of a flow of a network is the sum of all flows that gets produced in source $s$, or equivalent or the flows that is consumed in the sink $t$. +The value of a flow of a network is the sum of all flows that gets produced in source $s$, or equivalently of the flows that are consumed in the sink $t$. A **maximal flow** is a flow with the maximal possible value. Finding this maximal flow of a flow network is the problem that we want to solve. From 820c2b6fe8fd80a27362fc6eefe51a82684a1db8 Mon Sep 17 00:00:00 2001 From: Wiktor Kuchta Date: Fri, 1 Feb 2019 21:50:41 +0100 Subject: [PATCH 2/8] s/Lets/Let's/g --- src/algebra/fft.md | 2 +- src/data_structures/disjoint_set_union.md | 2 +- src/data_structures/segment_tree.md | 2 +- src/graph/2SAT.md | 2 +- src/graph/fixed_length_paths.md | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/algebra/fft.md b/src/algebra/fft.md index 958ca11e5..91b4b028d 100644 --- a/src/algebra/fft.md +++ b/src/algebra/fft.md @@ -80,7 +80,7 @@ $$A(x) = A_0(x^2) + x A_1(x^2).$$ The polynomials $A_0$ and $A_1$ are only half as much coefficients as the polynomial $A$. If we can compute the $\text{DFT}(A)$ in linear time using $\text{DFT}(A_0)$ and $\text{DFT}(A_1)$, then we get the recurrence $T_{\text{DFT}}(n) = 2 T_{\text{DFT}}\left(\frac{n}{2}\right) + O(n)$ for the time complexity, which results in $T_{\text{DFT}}(n) = O(n \log n)$ by the **master theorem**. -Lets learn how we can accomplish that. +Let's learn how we can accomplish that. Suppose we have computed the vectors $\left(y_k^0\right)\_{k=0}^{n/2-1} = \text{DFT}(A_0)$ and $\left(y_k^1\right)\_{k=0}^{n/2-1} = \text{DFT}(A_1)$. Let us find a expression for $\left(y_k\right)_{k=0}^{n-1} = \text{DFT}(A)$. diff --git a/src/data_structures/disjoint_set_union.md b/src/data_structures/disjoint_set_union.md index 3666b7bc3..56434f300 100644 --- a/src/data_structures/disjoint_set_union.md +++ b/src/data_structures/disjoint_set_union.md @@ -347,7 +347,7 @@ The only difficulty that we face is to compute the parity in the `union_find` me If we add an edge $(a, b)$ that connects two connected components into one, then when you attach one tree to another we need to adjust the parity. -Lets derive a formula, which computes the parity issued to the leader of the set that will get attached to another set. +Let's derive a formula, which computes the parity issued to the leader of the set that will get attached to another set. Let $x$ be the parity of the path length from vertex $a$ up to its leader $A$, and $y$ as the parity of the path length from vertex $b$ up to its leader $B$, and $t$ the desired parity that we have to assign to $B$ after the merge. The path contains the of the three parts: from $B$ to $b$, from $b$ to $a$, which is connected by one edge and therefore has parity $1$, and from $a$ to $A$. diff --git a/src/data_structures/segment_tree.md b/src/data_structures/segment_tree.md index 371c8b55d..77059896c 100644 --- a/src/data_structures/segment_tree.md +++ b/src/data_structures/segment_tree.md @@ -71,7 +71,7 @@ The time complexity of the construction is $O(n)$. For now we are going to answer sum queries. As an input we receive two integers $l$ and $r$, and we have to compute the sum of the segment $a[l \dots r]$ in $O(\log n)$ time. To do this, we will traverse the Segment Tree and use the precomputed sums of the segments. -Lets assume that we are currently at the vertex that covers the segment $a[tl \dots tr]$. +Let's assume that we are currently at the vertex that covers the segment $a[tl \dots tr]$. There are three possible cases. The easiest case is when the segment $a[l \dots r]$ is equal to the corresponding segment of the current vertex (i.e. $a[l \dots r] = a[tl \dots tr]$), then we are finished and can return the precomputed sum that is stored in the vertex. diff --git a/src/graph/2SAT.md b/src/graph/2SAT.md index e2858a207..df2d670a1 100644 --- a/src/graph/2SAT.md +++ b/src/graph/2SAT.md @@ -22,7 +22,7 @@ We now construct a directed graph of these implications: for each variable $x$ there will be two vertices $v_x$ and $v_{\lnot x}$. The edges will correspond to the implications. -Lets look at the example in 2-CNF form: +Let's look at the example in 2-CNF form: $$(a \lor \lnot b) \land (\lnot a \lor b) \land (\lnot a \lor \lnot b) \land (a \lor \lnot c)$$ diff --git a/src/graph/fixed_length_paths.md b/src/graph/fixed_length_paths.md index adee58ccd..d7ee69931 100644 --- a/src/graph/fixed_length_paths.md +++ b/src/graph/fixed_length_paths.md @@ -20,7 +20,7 @@ It is obvious that the constructed adjacency matrix if the answer to the problem It contains the number of paths of length $1$ between each pair of vertices. We will build the solution iteratively: -Lets assume we know the answer for some $k$. +Let's assume we know the answer for some $k$. Here we describe a method how we can construct the answer for $k + 1$. Denote by $C_k$ the matrix for the case $k$, and by $C_{k+1}$ the matrix we want to construct. With the following formula we can compute every entry of $C_{k+1}$: From cddebce9a79c8c3dcc43c9460dc3b8cad307b37b Mon Sep 17 00:00:00 2001 From: Wiktor Kuchta Date: Fri, 1 Feb 2019 19:31:50 +0100 Subject: [PATCH 3/8] totient: Fix inconsistent use of phi and varphi --- src/algebra/phi-function.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/algebra/phi-function.md b/src/algebra/phi-function.md index 9087f2798..e1eef0cb7 100644 --- a/src/algebra/phi-function.md +++ b/src/algebra/phi-function.md @@ -75,7 +75,7 @@ This allows computing $x^n \bmod m$ for very big $n$, especially if $n$ is the r There is a less known version of the last equivalence, that allows computing $x^n \bmod m$ efficiently for not coprime $x$ and $m$. For arbitrary $x, m$ and $n \geq \log_2 m$: -$$x^{n}\equiv x^{\varphi(m)+[n \bmod \varphi(m)]} \mod m$$ +$$x^{n}\equiv x^{\phi(m)+[n \bmod \phi(m)]} \mod m$$ Proof: @@ -100,7 +100,7 @@ $$x^n \bmod m = x^k\left(x^{n-k \bmod \phi(\frac{m}{a})} \bmod \frac{m}{a}\right This formula is difficult to apply, but we can use it to analyze the behavior of $x^n \bmod m$. We can see that the sequence of powers $(x^1 \bmod m, x^2 \bmod m, x^3 \bmod m, \dots)$ enters a cycle of length $\phi\left(\frac{m}{a}\right)$ after the first $k$ (or less) elements. $\phi(m)$ divides $\phi\left(\frac{m}{a}\right)$ (because $a$ and $\frac{m}{a}$ are coprime we have $\phi(a) \cdot \phi\left(\frac{m}{a}\right) = \phi(m)$), therefore we can also say that the period has length $\phi(m)$. And since $\phi(m) \ge \log_2 m \ge k$, we can conclude the desired, much simpler, formula: -$$ x^n \equiv x^{\varphi(m)} x^{(n - \varphi(m)) \bmod \varphi(m)} \bmod m \equiv x^{\varphi(m)+[n \bmod \varphi(m)]} \mod m.$$ +$$ x^n \equiv x^{\phi(m)} x^{(n - \phi(m)) \bmod \phi(m)} \bmod m \equiv x^{\phi(m)+[n \bmod \phi(m)]} \mod m.$$ ## Practice Problems From 2731dc23b68e46c8982254d21151aa7035b5e6a6 Mon Sep 17 00:00:00 2001 From: Wiktor Kuchta Date: Fri, 1 Feb 2019 21:27:19 +0100 Subject: [PATCH 4/8] Fix typos in discrete log --- src/algebra/discrete-log.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/algebra/discrete-log.md b/src/algebra/discrete-log.md index 19045d9ec..9d323a1a2 100644 --- a/src/algebra/discrete-log.md +++ b/src/algebra/discrete-log.md @@ -85,7 +85,7 @@ int solve(int a, int b, int m) { for (int p = n; p >= 1; --p) vals[powmod(a, p * n, m)] = p; for (int q = 0; q <= n; ++q) { - int cur = (powmod (a, q, m) * b) % m; + int cur = (powmod(a, q, m) * b) % m; if (vals.count(cur)) { int ans = vals[cur] * n - q; return ans; @@ -106,7 +106,10 @@ We also need to change the second step accordingly. ## Improved implementation -A possible improvement is to get rid of binary exponentiation. This can be done by keeping a variable that is multiplied by $a$ each time we increase $q$ and a variable that is multiplied by $a^n$ each time we increase $p$. With this change, the complexity of the algorithm is still the same, but now the $\log$ factor is only for `map`. Instead of a `map`, we can also use a hash table (`unordered_map` in C++) which has the average time complexity $O(1)$ for inserting and searching. +A possible improvement is to get rid of binary exponentiation. +This can be done by keeping a variable that is multiplied by $a$ each time we increase $q$ and a variable that is multiplied by $a^n$ each time we increase $p$. +With this change, the complexity of the algorithm is still the same, but now the $\log$ factor is only for the `map`. +Instead of a `map`, we can also use a hash table (`unordered_map` in C++) which has the average time complexity $O(1)$ for inserting and searching. ```cpp int solve(int a, int b, int m) { From 210d4fac0e8a93eb6664f7ffca6ba4476cfbc1b8 Mon Sep 17 00:00:00 2001 From: Wiktor Kuchta Date: Fri, 1 Feb 2019 21:37:22 +0100 Subject: [PATCH 5/8] Mention reducing the result in discrete log Probably better than the `ans < m` check, reasoning of which was lost in translation. --- src/algebra/discrete-log.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/algebra/discrete-log.md b/src/algebra/discrete-log.md index 9d323a1a2..f77bf2f1b 100644 --- a/src/algebra/discrete-log.md +++ b/src/algebra/discrete-log.md @@ -64,7 +64,9 @@ $$O(\sqrt {m} \log m).$$ ### The simplest implementation -In the following code, the function `powmod` calculates $a^b \pmod m$ and the function `solve` produces a proper solution to the problem. It returns $-1$ if there is no solution and returns one of the possible solutions otherwise. +In the following code, the function `powmod` calculates $a^b \pmod m$ and the function `solve` produces a proper solution to the problem. +It returns $-1$ if there is no solution and returns one of the possible solutions otherwise. +The resulting discrete logarithm can be big, but you can make it smaller using [Euler's theorem](./algebra/phi-function.html#toc-tgt-2). ```cpp int powmod(int a, int b, int m) { From 7cb31bfeaed527d60b08a26fad121fdc37bb37e1 Mon Sep 17 00:00:00 2001 From: Wiktor Kuchta Date: Fri, 1 Feb 2019 22:21:58 +0100 Subject: [PATCH 6/8] Improve code style in discrete root More consistent and modern, without type casting noise --- src/algebra/discrete-root.md | 84 ++++++++++++++++++++---------------- 1 file changed, 46 insertions(+), 38 deletions(-) diff --git a/src/algebra/discrete-root.md b/src/algebra/discrete-root.md index 21d916bbb..14c8fb51d 100644 --- a/src/algebra/discrete-root.md +++ b/src/algebra/discrete-root.md @@ -53,81 +53,89 @@ This is the final formula for all solutions of discrete root problem. Here is a full implementation, including routines for finding the primitive root, discrete log and finding and printing all solutions. ```cpp -int gcd (int a, int b) { - return a ? gcd (b%a, a) : b; +int gcd(int a, int b) { + return a ? gcd(b % a, a) : b; } -int powmod (int a, int b, int p) { +int powmod(int a, int b, int p) { int res = 1; - while (b) - if (b & 1) - res = int (res * 1ll * a % p), --b; - else - a = int (a * 1ll * a % p), b >>= 1; + while (b > 0) { + if (b & 1) { + res = res * a % p; + } + a = a * a % p; + b >>= 1; + } return res; } -int generator (int p) { +// Finds the primitive root modulo p +int generator(int p) { vector fact; - int phi = p-1, n = phi; - for (int i=2; i*i<=n; ++i) + int phi = p-1, n = phi; + for (int i = 2; i * i <= n; ++i) { if (n % i == 0) { - fact.push_back (i); + fact.push_back(i); while (n % i == 0) n /= i; } + } if (n > 1) - fact.push_back (n); + fact.push_back(n); - for (int res=2; res<=p; ++res) { + for (int res = 2; res <= p; ++res) { bool ok = true; - for (size_t i=0; i> n >> k >> a; + scanf("%d %d %d", &n, &k, &a); if (a == 0) { - puts ("1\n0"); + puts("1\n0"); return 0; } - int g = generator (n); + int g = generator(n); + // Baby-step giant-step discrete logarithm algorithm int sq = (int) sqrt (n + .0) + 1; - vector < pair > dec (sq); - for (int i=1; i<=sq; ++i) - dec[i-1] = make_pair (powmod (g, int (i * sq * 1ll * k % (n - 1)), n), i); - sort (dec.begin(), dec.end()); + vector> dec(sq); + for (int i = 1; i <= sq; ++i) + dec[i-1] = {powmod(g, i * sq * k % (n - 1), n), i}; + sort(dec.begin(), dec.end()); int any_ans = -1; - for (int i=0; i >::iterator it = - lower_bound (dec.begin(), dec.end(), make_pair (my, 0)); + for (int i = 0; i < sq; ++i) { + int my = powmod(g, i * k % (n - 1), n) * a % n; + auto it = lower_bound(dec.begin(), dec.end(), make_pair(my, 0)); if (it != dec.end() && it->first == my) { any_ans = it->second * sq - i; break; } } if (any_ans == -1) { - puts ("0"); + puts("0"); return 0; } - int delta = (n-1) / gcd (k, n-1); + // Print all possible answers + int delta = (n-1) / gcd(k, n-1); vector ans; - for (int cur=any_ans%delta; cur Date: Fri, 1 Feb 2019 22:23:07 +0100 Subject: [PATCH 7/8] Fix typos in discrete root --- src/algebra/discrete-root.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/algebra/discrete-root.md b/src/algebra/discrete-root.md index 14c8fb51d..8494737ef 100644 --- a/src/algebra/discrete-root.md +++ b/src/algebra/discrete-root.md @@ -2,7 +2,7 @@ # Discrete Root -The problem of finding discrete root is defined as follows. Given a prime $n$ and two integers $a$ and $k$, find all $x$ for which: +The problem of finding a discrete root is defined as follows. Given a prime $n$ and two integers $a$ and $k$, find all $x$ for which: $x^k \equiv a \pmod n$ @@ -14,7 +14,7 @@ Let's apply the concept of a [primitive root](./algebra/primitive-root.html) mod We can easily discard the case where $a = 0$. In this case, obviously there is only one answer: $x = 0$. -Since we jnow that $n$ is a prime, any number between 1 and $n-1$ can be represented as a power of the primitive root, and we can represent the discrete root problem as follows: +Since we know that $n$ is a prime and any number between 1 and $n-1$ can be represented as a power of the primitive root, we can represent the discrete root problem as follows: $(g^y)^k \equiv a \pmod n$ @@ -26,7 +26,7 @@ This, in turn, can be rewritten as $(g^k)^y \equiv a \pmod n$ -Now we have one unknown $y$, which is a discrete logarithm problem. The solution can be found using Shanks' baby-step-giant-step algorithm in $O(\sqrt {n} \log n)$ (or we can verify that there are no solutions). +Now we have one unknown $y$, which is a discrete logarithm problem. The solution can be found using Shanks' baby-step giant-step algorithm in $O(\sqrt {n} \log n)$ (or we can verify that there are no solutions). Having found one solution $y_0$, one of solutions of discrete root problem will be $x_0 = g^{y_0} \pmod n$. @@ -46,11 +46,11 @@ where $l$ is chosen such that the fraction must be an integer. For this to be tr $x = g^{y_0 + i \frac {\phi (n)}{gcd(k, \phi (n))}} \pmod n \forall i \in Z$. -This is the final formula for all solutions of discrete root problem. +This is the final formula for all solutions of the discrete root problem. ## Implementation -Here is a full implementation, including routines for finding the primitive root, discrete log and finding and printing all solutions. +Here is a full implementation, including procedures for finding the primitive root, discrete log and finding and printing all solutions. ```cpp int gcd(int a, int b) { From 40f9c37d4b677aaffe24b0d0e3e500a87c0f2349 Mon Sep 17 00:00:00 2001 From: Wiktor Kuchta Date: Fri, 1 Feb 2019 22:26:40 +0100 Subject: [PATCH 8/8] Add practice problem to discrete root --- src/algebra/discrete-root.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/algebra/discrete-root.md b/src/algebra/discrete-root.md index 8494737ef..daab06030 100644 --- a/src/algebra/discrete-root.md +++ b/src/algebra/discrete-root.md @@ -139,3 +139,6 @@ int main() { } ``` +## Practice problems + +* [Codeforces - Lunar New Year and a Recursive Sequence](https://codeforces.com/contest/1106/problem/F) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy