Skip to content

Commit

Permalink
Improve language
Browse files Browse the repository at this point in the history
  • Loading branch information
pllk committed May 31, 2017
1 parent 6a9049f commit 7c09ec1
Showing 1 changed file with 58 additions and 63 deletions.
121 changes: 58 additions & 63 deletions chapter28.tex
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ \chapter{Segment trees revisited}
\index{segment tree}

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
that we have not touched yet.
Now is time to discuss some more advanced variants
Expand All @@ -12,53 +12,52 @@ \chapter{Segment trees revisited}
So far, we have implemented the operations
of a segment tree by walking \emph{from bottom to top}
in the tree.
For example, we have calculated the sum of
elements in a range $[a,b]$
as follows (Chapter 9.3):
For example, we have calculated
range sums as follows (Chapter 9.3):

\begin{lstlisting}
int sum(int a, int b) {
a += N; b += N;
a += n; b += n;
int s = 0;
while (a <= b) {
if (a%2 == 1) s += p[a++];
if (b%2 == 0) s += p[b--];
if (a%2 == 1) s += tree[a++];
if (b%2 == 0) s += tree[b--];
a /= 2; b /= 2;
}
return s;
}
\end{lstlisting}

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}.
Using this approach, the function becomes as follows:

\begin{lstlisting}
int sum(int a, int b, int k, int x, int y) {
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;
return sum(a,b,2*k,x,d) + sum(a,b,2*k+1,d+1,y);
}
\end{lstlisting}

Now we can calculate the sum of
elements in $[a,b]$ as follows:
Now we can calculate any value of $\texttt{sum}_q(a,b)$
(the sum of array values in range $[a,b]$) as follows:
\begin{lstlisting}
int s = sum(a, b, 1, 0, N-1);
int s = sum(a, b, 1, 0, n-1);
\end{lstlisting}

The parameter $k$ indicates the current position
in \texttt{p}.
in \texttt{tree}.
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$
and is initially $[0,N-1]$.
and is initially $[0,n-1]$.
When calculating the sum,
if $[x,y]$ is outside $[a,b]$,
the sum is 0,
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]$,
the search continues recursively to the
left and right half of $[x,y]$.
Expand All @@ -67,9 +66,9 @@ \chapter{Segment trees revisited}
where $d=\lfloor \frac{x+y}{2} \rfloor$.

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
stops and the sum can be found in \texttt{p}.
stops and the sum can be found in \texttt{tree}.

\begin{center}
\begin{tikzpicture}[scale=0.7]
Expand Down Expand Up @@ -191,7 +190,7 @@ \section{Lazy propagation}
propagated to its children.

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
or \emph{assigned} some value.
Both operations can be implemented using
Expand All @@ -202,17 +201,17 @@ \subsubsection{Lazy segment trees}

Let us consider an example where our goal is to
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
elements in $[a,b]$.
values in $[a,b]$.

We will construct a tree where each node
contains two values $s/z$:
$s$ denotes the sum of elements in the range
has two values $s/z$:
$s$ denotes the sum of values in the range,
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$.
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.
\begin{center}
\begin{tikzpicture}[scale=0.7]
Expand Down Expand Up @@ -290,7 +289,7 @@ \subsubsection{Lazy segment trees}
we walk from the root towards the leaves
and modify the nodes of the tree as follows:
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.
If $[x,y]$ only partially belongs to $[a,b]$,
we increase the $s$ value of the node by $hu$,
Expand Down Expand Up @@ -406,10 +405,9 @@ \subsubsection{Lazy segment trees}
which guarantees that the operations are always efficient.

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,
because a lazy update is propagated downwards,
which is necessary to calculate the sum in $[a,b]$.
because a lazy update is propagated downwards.

\begin{center}
\begin{tikzpicture}[scale=0.7]
Expand Down Expand Up @@ -501,7 +499,7 @@ \subsubsection{Lazy segment trees}
Note that sometimes it is needed to combine lazy updates.
This happens when a node that already has a 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$
corresponds to an update $z_1+z_2$.

Expand All @@ -511,13 +509,12 @@ \subsubsection{Polynomial updates}
possible to update ranges using polynomials of the form
\[p(u) = t_k u^k + t_{k-1} u^{k-1} + \cdots + t_0.\]

In this case, the update for an element
at position $i$
in the range $[a,b]$ is $p(i-a)$.
In this case, the update for a value
at position $i$ in $[a,b]$ is $p(i-a)$.
For example, adding the polynomial $p(u)=u+1$
to the range $[a,b]$ means that the element at position $a$
is increased by 1, the element at position $a+1$
is increased by 2, and so on.
to $[a,b]$ means that the value at position $a$
increases by 1, the value at position $a+1$
increases by 2, and so on.

To support polynomial updates,
each node is assigned $k+2$ values,
Expand All @@ -526,7 +523,7 @@ \subsubsection{Polynomial updates}
and the values $z_0,z_1,\ldots,z_k$ are the coefficients
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.\]

The value of such a sum
Expand Down Expand Up @@ -560,27 +557,25 @@ \section{Dynamic trees}
which can save a large amount of memory.

The nodes of a dynamic tree can be represented as structs:

\begin{lstlisting}
struct node {
int v;
int value;
int x, y;
node *l, *r;
node(int v, int x, int y) : v(v), x(x), y(y) {}
node *left, *right;
node(int v, int x, int y) : value(v), x(x), y(y) {}
};
\end{lstlisting}
Here $v$ is the value of the node,
$[x,y]$ is the corresponding range,
and $l$ and $r$ point to the left
Here \texttt{value} is the value of the node,
$[\texttt{x},\texttt{y}]$ is the corresponding range,
and \texttt{left} and \texttt{right} point to the left
and right subtree.

After this, nodes can be created as follows:

\begin{lstlisting}
// create new node
node *u = new node(0, 0, 15);
node *x = new node(0, 0, 15);
// change value
u->v = 5;
x->value = 5;
\end{lstlisting}

\subsubsection{Sparse segment trees}
Expand All @@ -589,19 +584,19 @@ \subsubsection{Sparse segment trees}

A dynamic segment tree is useful when
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,
but most array values are zeros.
While an ordinary segment tree uses $O(N)$ memory,
a dynamic segment tree only uses $O(n \log N)$ memory,
where $n$ is the number of operations performed.
While an ordinary segment tree uses $O(n)$ memory,
a dynamic segment tree only uses $O(k \log n)$ memory,
where $k$ is the number of operations performed.

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.
After updates, new nodes are dynamically added
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,
the tree contains the following nodes:
\begin{center}
Expand Down Expand Up @@ -630,16 +625,16 @@ \subsubsection{Sparse segment trees}
\end{center}

Any path from the root node to a leaf contains
$O(\log N)$ nodes,
so each operation adds at most $O(\log N)$
$O(\log n)$ nodes,
so each operation adds at most $O(\log n)$
new nodes to the tree.
Thus, after $n$ operations, the tree contains
at most $O(n \log N)$ nodes.
Thus, after $k$ operations, the tree contains
at most $O(k \log n)$ nodes.

Note that if all indices of the elements
are known at the beginning of the algorithm,
Note that if we know all elements to be updated
at the beginning of the algorithm,
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).
However, this is not possible when the indices
are generated during the algorithm.
Expand Down Expand Up @@ -773,8 +768,8 @@ \subsubsection{Persistent segment trees}
The structure of each previous tree can be
reconstructed by following the pointers
starting at the corresponding root node.
Since each operation during the algorithm
adds only $O(\log N)$ new nodes to the tree,
Since each operation
adds only $O(\log n)$ new nodes to the tree,
it is possible to store the full modification history of the tree.

\section{Data structures}
Expand Down

0 comments on commit 7c09ec1

Please sign in to comment.