Improve language

This commit is contained in:
Antti H S Laaksonen 2017-05-31 22:59:00 +03:00
parent 6a9049f35b
commit 7c09ec17d3
1 changed files with 58 additions and 63 deletions

View File

@ -3,7 +3,7 @@
\index{segment tree} \index{segment tree}
A segment tree is a versatile data structure A segment tree is a versatile data structure
that can be used in a large number of problems. that can be used to solve a large number of algorithm problems.
However, there are many topics related to segment trees However, there are many topics related to segment trees
that we have not touched yet. that we have not touched yet.
Now is time to discuss some more advanced variants Now is time to discuss some more advanced variants
@ -12,17 +12,16 @@ of segment trees.
So far, we have implemented the operations So far, we have implemented the operations
of a segment tree by walking \emph{from bottom to top} of a segment tree by walking \emph{from bottom to top}
in the tree. in the tree.
For example, we have calculated the sum of For example, we have calculated
elements in a range $[a,b]$ range sums as follows (Chapter 9.3):
as follows (Chapter 9.3):
\begin{lstlisting} \begin{lstlisting}
int sum(int a, int b) { int sum(int a, int b) {
a += N; b += N; a += n; b += n;
int s = 0; int s = 0;
while (a <= b) { while (a <= b) {
if (a%2 == 1) s += p[a++]; if (a%2 == 1) s += tree[a++];
if (b%2 == 0) s += p[b--]; if (b%2 == 0) s += tree[b--];
a /= 2; b /= 2; a /= 2; b /= 2;
} }
return s; return s;
@ -30,35 +29,35 @@ int sum(int a, int b) {
\end{lstlisting} \end{lstlisting}
However, in more advanced segment trees, However, in more advanced segment trees,
it is often needed to implement the operations it is often necessary to implement the operations
in another way, \emph{from top to bottom}. in another way, \emph{from top to bottom}.
Using this approach, the function becomes as follows: Using this approach, the function becomes as follows:
\begin{lstlisting} \begin{lstlisting}
int sum(int a, int b, int k, int x, int y) { int sum(int a, int b, int k, int x, int y) {
if (b < x || a > y) return 0; if (b < x || a > y) return 0;
if (a <= x && y <= b) return p[k]; if (a <= x && y <= b) return tree[k];
int d = (x+y)/2; int d = (x+y)/2;
return sum(a,b,2*k,x,d) + sum(a,b,2*k+1,d+1,y); return sum(a,b,2*k,x,d) + sum(a,b,2*k+1,d+1,y);
} }
\end{lstlisting} \end{lstlisting}
Now we can calculate the sum of Now we can calculate any value of $\texttt{sum}_q(a,b)$
elements in $[a,b]$ as follows: (the sum of array values in range $[a,b]$) as follows:
\begin{lstlisting} \begin{lstlisting}
int s = sum(a, b, 1, 0, N-1); int s = sum(a, b, 1, 0, n-1);
\end{lstlisting} \end{lstlisting}
The parameter $k$ indicates the current position The parameter $k$ indicates the current position
in \texttt{p}. in \texttt{tree}.
Initially $k$ equals 1, because we begin Initially $k$ equals 1, because we begin
at the root of the segment tree. at the root of the tree.
The range $[x,y]$ corresponds to $k$ The range $[x,y]$ corresponds to $k$
and is initially $[0,N-1]$. and is initially $[0,n-1]$.
When calculating the sum, When calculating the sum,
if $[x,y]$ is outside $[a,b]$, if $[x,y]$ is outside $[a,b]$,
the sum is 0, the sum is 0,
and if $[x,y]$ is completely inside $[a,b]$, and if $[x,y]$ is completely inside $[a,b]$,
the sum can be found in \texttt{p}. the sum can be found in \texttt{tree}.
If $[x,y]$ is partially inside $[a,b]$, If $[x,y]$ is partially inside $[a,b]$,
the search continues recursively to the the search continues recursively to the
left and right half of $[x,y]$. left and right half of $[x,y]$.
@ -67,9 +66,9 @@ and the right half is $[d+1,y]$
where $d=\lfloor \frac{x+y}{2} \rfloor$. where $d=\lfloor \frac{x+y}{2} \rfloor$.
The following picture shows how the search proceeds The following picture shows how the search proceeds
when calculating the sum of elements in $[a,b]$. when calculating the value of $\texttt{sum}_q(a,b)$.
The gray nodes indicate nodes where the recursion The gray nodes indicate nodes where the recursion
stops and the sum can be found in \texttt{p}. stops and the sum can be found in \texttt{tree}.
\begin{center} \begin{center}
\begin{tikzpicture}[scale=0.7] \begin{tikzpicture}[scale=0.7]
@ -191,7 +190,7 @@ related to lazy updates, which has not been
propagated to its children. propagated to its children.
There are two types of range updates: There are two types of range updates:
each element in the range is either each array value in the range is either
\emph{increased} by some value \emph{increased} by some value
or \emph{assigned} some value. or \emph{assigned} some value.
Both operations can be implemented using Both operations can be implemented using
@ -202,17 +201,17 @@ a tree that supports both operations at the same time.
Let us consider an example where our goal is to Let us consider an example where our goal is to
construct a segment tree that supports construct a segment tree that supports
two operations: increasing each element in two operations: increasing each value in
$[a,b]$ by a constant and calculating the sum of $[a,b]$ by a constant and calculating the sum of
elements in $[a,b]$. values in $[a,b]$.
We will construct a tree where each node We will construct a tree where each node
contains two values $s/z$: has two values $s/z$:
$s$ denotes the sum of elements in the range $s$ denotes the sum of values in the range,
and $z$ denotes the value of a lazy update, and $z$ denotes the value of a lazy update,
which means that all elements in the range which means that all values in the range
should be increased by $z$. should be increased by $z$.
In the following tree, $z=0$ for all nodes, In the following tree, $z=0$ in all nodes,
so there are no ongoing lazy updates. so there are no ongoing lazy updates.
\begin{center} \begin{center}
\begin{tikzpicture}[scale=0.7] \begin{tikzpicture}[scale=0.7]
@ -290,7 +289,7 @@ When the elements in $[a,b]$ are increased by $u$,
we walk from the root towards the leaves we walk from the root towards the leaves
and modify the nodes of the tree as follows: and modify the nodes of the tree as follows:
If the range $[x,y]$ of a node is If the range $[x,y]$ of a node is
completely inside the range $[a,b]$, completely inside $[a,b]$,
we increase the $z$ value of the node by $u$ and stop. we increase the $z$ value of the node by $u$ and stop.
If $[x,y]$ only partially belongs to $[a,b]$, If $[x,y]$ only partially belongs to $[a,b]$,
we increase the $s$ value of the node by $hu$, we increase the $s$ value of the node by $hu$,
@ -406,10 +405,9 @@ downwards only when it is necessary,
which guarantees that the operations are always efficient. which guarantees that the operations are always efficient.
The following picture shows how the tree changes The following picture shows how the tree changes
when we calculate the sum of elements in $[a,b]$. when we calculate the value of $\texttt{sum}_a(a,b)$.
The rectangle shows the nodes whose values change, The rectangle shows the nodes whose values change,
because a lazy update is propagated downwards, because a lazy update is propagated downwards.
which is necessary to calculate the sum in $[a,b]$.
\begin{center} \begin{center}
\begin{tikzpicture}[scale=0.7] \begin{tikzpicture}[scale=0.7]
@ -501,7 +499,7 @@ which is necessary to calculate the sum in $[a,b]$.
Note that sometimes it is needed to combine lazy updates. Note that sometimes it is needed to combine lazy updates.
This happens when a node that already has a lazy update This happens when a node that already has a lazy update
is assigned another lazy update. is assigned another lazy update.
In this problem, it is easy to combine lazy updates, When calculating sums, it is easy to combine lazy updates,
because the combination of updates $z_1$ and $z_2$ because the combination of updates $z_1$ and $z_2$
corresponds to an update $z_1+z_2$. corresponds to an update $z_1+z_2$.
@ -511,13 +509,12 @@ Lazy updates can be generalized so that it is
possible to update ranges using polynomials of the form possible to update ranges using polynomials of the form
\[p(u) = t_k u^k + t_{k-1} u^{k-1} + \cdots + t_0.\] \[p(u) = t_k u^k + t_{k-1} u^{k-1} + \cdots + t_0.\]
In this case, the update for an element In this case, the update for a value
at position $i$ at position $i$ in $[a,b]$ is $p(i-a)$.
in the range $[a,b]$ is $p(i-a)$.
For example, adding the polynomial $p(u)=u+1$ For example, adding the polynomial $p(u)=u+1$
to the range $[a,b]$ means that the element at position $a$ to $[a,b]$ means that the value at position $a$
is increased by 1, the element at position $a+1$ increases by 1, the value at position $a+1$
is increased by 2, and so on. increases by 2, and so on.
To support polynomial updates, To support polynomial updates,
each node is assigned $k+2$ values, each node is assigned $k+2$ values,
@ -526,7 +523,7 @@ The value $s$ is the sum of the elements in the range,
and the values $z_0,z_1,\ldots,z_k$ are the coefficients and the values $z_0,z_1,\ldots,z_k$ are the coefficients
of a polynomial that corresponds to a lazy update. of a polynomial that corresponds to a lazy update.
Now, the sum of elements in a range $[x,y]$ equals Now, the sum of values in a range $[x,y]$ equals
\[s+\sum_{u=0}^{y-x} z_k u^k + z_{k-1} u^{k-1} + \cdots + z_0.\] \[s+\sum_{u=0}^{y-x} z_k u^k + z_{k-1} u^{k-1} + \cdots + z_0.\]
The value of such a sum The value of such a sum
@ -560,27 +557,25 @@ are actually accessed during the algorithm,
which can save a large amount of memory. which can save a large amount of memory.
The nodes of a dynamic tree can be represented as structs: The nodes of a dynamic tree can be represented as structs:
\begin{lstlisting} \begin{lstlisting}
struct node { struct node {
int v; int value;
int x, y; int x, y;
node *l, *r; node *left, *right;
node(int v, int x, int y) : v(v), x(x), y(y) {} node(int v, int x, int y) : value(v), x(x), y(y) {}
}; };
\end{lstlisting} \end{lstlisting}
Here $v$ is the value of the node, Here \texttt{value} is the value of the node,
$[x,y]$ is the corresponding range, $[\texttt{x},\texttt{y}]$ is the corresponding range,
and $l$ and $r$ point to the left and \texttt{left} and \texttt{right} point to the left
and right subtree. and right subtree.
After this, nodes can be created as follows: After this, nodes can be created as follows:
\begin{lstlisting} \begin{lstlisting}
// create new node // create new node
node *u = new node(0, 0, 15); node *x = new node(0, 0, 15);
// change value // change value
u->v = 5; x->value = 5;
\end{lstlisting} \end{lstlisting}
\subsubsection{Sparse segment trees} \subsubsection{Sparse segment trees}
@ -589,19 +584,19 @@ u->v = 5;
A dynamic segment tree is useful when A dynamic segment tree is useful when
the underlying array is \emph{sparse}, the underlying array is \emph{sparse},
i.e., the range $[0,N-1]$ i.e., the range $[0,n-1]$
of allowed indices is large, of allowed indices is large,
but most array values are zeros. but most array values are zeros.
While an ordinary segment tree uses $O(N)$ memory, While an ordinary segment tree uses $O(n)$ memory,
a dynamic segment tree only uses $O(n \log N)$ memory, a dynamic segment tree only uses $O(k \log n)$ memory,
where $n$ is the number of operations performed. where $k$ is the number of operations performed.
A \key{sparse segment tree} initially has A \key{sparse segment tree} initially has
only one node $[0,N-1]$ whose value is zero, only one node $[0,n-1]$ whose value is zero,
which means that every array value is zero. which means that every array value is zero.
After updates, new nodes are dynamically added After updates, new nodes are dynamically added
to the tree. to the tree.
For example, if $N=16$ and the elements For example, if $n=16$ and the elements
in positions 3 and 10 have been modified, in positions 3 and 10 have been modified,
the tree contains the following nodes: the tree contains the following nodes:
\begin{center} \begin{center}
@ -630,16 +625,16 @@ the tree contains the following nodes:
\end{center} \end{center}
Any path from the root node to a leaf contains Any path from the root node to a leaf contains
$O(\log N)$ nodes, $O(\log n)$ nodes,
so each operation adds at most $O(\log N)$ so each operation adds at most $O(\log n)$
new nodes to the tree. new nodes to the tree.
Thus, after $n$ operations, the tree contains Thus, after $k$ operations, the tree contains
at most $O(n \log N)$ nodes. at most $O(k \log n)$ nodes.
Note that if all indices of the elements Note that if we know all elements to be updated
are known at the beginning of the algorithm, at the beginning of the algorithm,
a dynamic segment tree is not necessary, a dynamic segment tree is not necessary,
but we can use an ordinary segment tree with because we can use an ordinary segment tree with
index compression (Chapter 9.4). index compression (Chapter 9.4).
However, this is not possible when the indices However, this is not possible when the indices
are generated during the algorithm. are generated during the algorithm.
@ -773,8 +768,8 @@ stored as follows:
The structure of each previous tree can be The structure of each previous tree can be
reconstructed by following the pointers reconstructed by following the pointers
starting at the corresponding root node. starting at the corresponding root node.
Since each operation during the algorithm Since each operation
adds only $O(\log N)$ new nodes to the tree, adds only $O(\log n)$ new nodes to the tree,
it is possible to store the full modification history of the tree. it is possible to store the full modification history of the tree.
\section{Data structures} \section{Data structures}