Corrections
This commit is contained in:
parent
8ce03c2145
commit
e3dfd6ebf1
335
luku28.tex
335
luku28.tex
|
@ -5,14 +5,15 @@
|
||||||
A segment tree is a versatile data structure
|
A segment tree is a versatile data structure
|
||||||
that can be used in many different situations.
|
that can be used in many different situations.
|
||||||
However, there are many topics related to segment trees
|
However, there are many topics related to segment trees
|
||||||
that we haven't touched yet.
|
that we have not touched yet.
|
||||||
Now it's time to learn some more advanced variations
|
Now it is time to discuss some more advanced variants
|
||||||
of segment trees and see their full potential.
|
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 the bottom to the top},
|
of a segment tree by walking \emph{from bottom to top}
|
||||||
from the leaves to the root.
|
in the tree.
|
||||||
For example, we have calculated the sum of a range $[a,b]$
|
For example, we have calculated the sum of
|
||||||
|
elements in a range $[a,b]$
|
||||||
as follows (Chapter 9.3):
|
as follows (Chapter 9.3):
|
||||||
|
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
|
@ -28,9 +29,8 @@ int sum(int a, int b) {
|
||||||
}
|
}
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
However, in more advanced segment trees,
|
However, in more advanced segment trees,
|
||||||
it's beneficial to implement the operations
|
it is often needed to implement the operations
|
||||||
in another way, \emph{from the top to the bottom},
|
in another way, \emph{from top to bottom}.
|
||||||
from the root to the leaves.
|
|
||||||
Using this approach, the function becomes as follows:
|
Using this approach, the function becomes as follows:
|
||||||
|
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
|
@ -42,34 +42,38 @@ int sum(int a, int b, int k, int x, int y) {
|
||||||
sum(max(x+d,a), b, 2*k+1, x+d, y);
|
sum(max(x+d,a), b, 2*k+1, x+d, y);
|
||||||
}
|
}
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
Now we can calulate the sum of the range $[a,b]$
|
Now we can calculate the sum of
|
||||||
as follows:
|
elements in $[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$ is the current position
|
|
||||||
in array \texttt{p}.
|
\begin{samepage}
|
||||||
|
The parameter $k$ indicates the current position
|
||||||
|
in \texttt{p}.
|
||||||
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 segment 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]$.
|
||||||
If $[a,b]$ is outside $[x,y]$,
|
When calculating the sum,
|
||||||
the sum of the range is 0,
|
if $[a,b]$ is outside $[x,y]$,
|
||||||
|
the sum is 0,
|
||||||
and if $[a,b]$ equals $[x,y]$,
|
and if $[a,b]$ equals $[x,y]$,
|
||||||
the sum can be found in array \texttt{p}.
|
the sum can be found in \texttt{p}.
|
||||||
If $[a,b]$ is completely or partially inside $[x,y]$,
|
If $[a,b]$ is completely or partially inside $[x,y]$,
|
||||||
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]$.
|
||||||
The size of both halves is $d=\frac{1}{2}(y-x+1)$;
|
The size of both halves is $d=\frac{1}{2}(y-x+1)$;
|
||||||
the left half is $[x,x+d-1]$
|
the left half is $[x,x+d-1]$
|
||||||
and the right half is $[x+d,y]$.
|
and the right half is $[x+d,y]$.
|
||||||
|
\end{samepage}
|
||||||
|
|
||||||
The following picture shows how the search proceeds
|
The following picture shows how the search proceeds
|
||||||
when calculating the sum of the marked elements.
|
when calculating the sum of elements in $[a,b]$.
|
||||||
The gray nodes indicate nodes where the recursion
|
The gray nodes indicate nodes where the recursion
|
||||||
stops and the sum of the range can be found in array \texttt{p}.
|
stops and the sum can be found in \texttt{p}.
|
||||||
\\
|
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\begin{tikzpicture}[scale=0.7]
|
\begin{tikzpicture}[scale=0.7]
|
||||||
\fill[color=gray!50] (5,0) rectangle (6,1);
|
\fill[color=gray!50] (5,0) rectangle (6,1);
|
||||||
|
@ -157,63 +161,61 @@ stops and the sum of the range can be found in array \texttt{p}.
|
||||||
\path[draw=red,thick,->,line width=2pt] (l) -- (g);
|
\path[draw=red,thick,->,line width=2pt] (l) -- (g);
|
||||||
|
|
||||||
\draw [decoration={brace}, decorate, line width=0.5mm] (14,-0.25) -- (5,-0.25);
|
\draw [decoration={brace}, decorate, line width=0.5mm] (14,-0.25) -- (5,-0.25);
|
||||||
|
|
||||||
|
\node at (5.5,-0.75) {$a$};
|
||||||
|
\node at (13.5,-0.75) {$b$};
|
||||||
\end{tikzpicture}
|
\end{tikzpicture}
|
||||||
\end{center}
|
\end{center}
|
||||||
Also in this implementation,
|
Also in this implementation,
|
||||||
the time complexity of a range query is $O(\log n)$,
|
operations take $O(\log n)$ time,
|
||||||
because the total number of processed nodes is $O(\log n)$.
|
because the total number of visited nodes is $O(\log n)$.
|
||||||
|
|
||||||
\section{Lazy propagation}
|
\section{Lazy propagation}
|
||||||
|
|
||||||
\index{lazy propagation}
|
\index{lazy propagation}
|
||||||
\index{lazy segment tree}
|
\index{lazy segment tree}
|
||||||
|
|
||||||
Using \key{lazy propagation}, we can construct
|
Using \key{lazy propagation}, we can build
|
||||||
a segment tree that supports both range updates
|
a segment tree that supports both range updates
|
||||||
and range queries in $O(\log n)$ time.
|
and range queries in $O(\log n)$ time.
|
||||||
The idea is to perform the updates and queries
|
The idea is to perform updates and queries
|
||||||
from the top to the bottom, and process the updates
|
from top to bottom and perform updates
|
||||||
\emph{lazily} so that they are propagated
|
\emph{lazily} so that they are propagated
|
||||||
down the tree only when it is necessary.
|
down the tree only when it is necessary.
|
||||||
|
|
||||||
In a lazy segment tree, nodes contain two types of
|
In a lazy segment tree, nodes contain two types of
|
||||||
information.
|
information.
|
||||||
Like in a normal segment tree,
|
Like in an ordinary segment tree,
|
||||||
each node contains the sum or some other value
|
each node contains the sum or some other value
|
||||||
of the corresponding subarray.
|
related to the corresponding subarray.
|
||||||
In addition, the node may contain information
|
In addition, the node may contain information
|
||||||
related to lazy updates, which has not been
|
related to lazy updates, which has not been
|
||||||
propagated yet to its children.
|
propagated to its children.
|
||||||
|
|
||||||
There are two possible types for range updates:
|
There are two possible types of range updates:
|
||||||
\emph{addition} and \emph{insertion}.
|
each element in the range is either
|
||||||
In addition, each element in the range is
|
\emph{increased} by some value
|
||||||
increased by some value,
|
or \emph{assigned} some value.
|
||||||
and in insertion, each element in the range
|
|
||||||
is assigned some value.
|
|
||||||
Both operations can be implemented using
|
Both operations can be implemented using
|
||||||
similar ideas, and it's possible to construct
|
similar ideas, and it is even possible to construct
|
||||||
a tree that supports both the operations
|
a tree that supports both operations at the same time.
|
||||||
simultaneously.
|
|
||||||
|
|
||||||
\subsubsection{Lazy segment tree}
|
\subsubsection{Lazy segment tree}
|
||||||
|
|
||||||
Let's consider an example where our goal is to
|
Let us consider an example where our goal is to
|
||||||
construct a segment tree that supports the following operations:
|
construct a segment tree that supports
|
||||||
|
two operations: increasing each element in
|
||||||
|
$[a,b]$ by $u$ and calculating the sum of
|
||||||
|
elements in $[a,b]$.
|
||||||
|
|
||||||
\begin{itemize}
|
|
||||||
\item increase each element in $[a,b]$ by $u$
|
|
||||||
\item calculate the sum of elements in $[a,b]$
|
|
||||||
\end{itemize}
|
|
||||||
We will construct a tree where each node
|
We will construct a tree where each node
|
||||||
contains two values $s/z$:
|
contains two values $s/z$:
|
||||||
$s$ denotes the sum of elements in the range,
|
$s$ denotes the sum of elements in the range
|
||||||
like in a standard segment tree,
|
and $z$ denotes the value of a lazy update,
|
||||||
and $z$ denotes a lazy update,
|
|
||||||
which means that all elements in the range
|
which means that all elements 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$ for all nodes,
|
||||||
so there are no lazy updates.
|
so there are no ongoing lazy updates.
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\begin{tikzpicture}[scale=0.7]
|
\begin{tikzpicture}[scale=0.7]
|
||||||
\draw (0,0) grid (16,1);
|
\draw (0,0) grid (16,1);
|
||||||
|
@ -286,19 +288,19 @@ so there are no lazy updates.
|
||||||
\end{tikzpicture}
|
\end{tikzpicture}
|
||||||
\end{center}
|
\end{center}
|
||||||
|
|
||||||
When a range $[a,b]$ is increased by $u$,
|
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 in the tree as follows:
|
and modify the nodes in 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 the range $[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.
|
||||||
However, 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$,
|
||||||
where $h$ is the size of the intersection of $[a,b]$
|
where $h$ is the size of the intersection of $[a,b]$
|
||||||
and $[x,y]$, and continue our walk recursively in the tree.
|
and $[x,y]$, and continue our walk recursively in the tree.
|
||||||
|
|
||||||
For example, the following picture shows the tree after
|
For example, the following picture shows the tree after
|
||||||
increasing the elements in the range marked at the bottom by 2:
|
increasing the elements in the range $[a,b]$ by 2:
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\begin{tikzpicture}[scale=0.7]
|
\begin{tikzpicture}[scale=0.7]
|
||||||
\fill[color=gray!50] (5,0) rectangle (6,1);
|
\fill[color=gray!50] (5,0) rectangle (6,1);
|
||||||
|
@ -384,27 +386,33 @@ increasing the elements in the range marked at the bottom by 2:
|
||||||
\path[draw=red,thick,->,line width=2pt] (l) -- (g);
|
\path[draw=red,thick,->,line width=2pt] (l) -- (g);
|
||||||
|
|
||||||
\draw [decoration={brace}, decorate, line width=0.5mm] (14,-0.25) -- (5,-0.25);
|
\draw [decoration={brace}, decorate, line width=0.5mm] (14,-0.25) -- (5,-0.25);
|
||||||
|
|
||||||
|
\node at (5.5,-0.75) {$a$};
|
||||||
|
\node at (13.5,-0.75) {$b$};
|
||||||
\end{tikzpicture}
|
\end{tikzpicture}
|
||||||
\end{center}
|
\end{center}
|
||||||
|
|
||||||
We also calculate the sum in a range $[a,b]$
|
We also calculate the sum of elements in a range $[a,b]$
|
||||||
by walking in the tree from the root towards the leaves.
|
by walking in the tree from top to bottom.
|
||||||
If the range $[x,y]$ of a node completely belongs
|
If the range $[x,y]$ of a node completely belongs
|
||||||
to $[a,b]$, we add the $s$ value of the node to the sum.
|
to $[a,b]$, we add the $s$ value of the node to the sum.
|
||||||
Otherwise, we continue the search recursively
|
Otherwise, we continue the search recursively
|
||||||
downwards in the tree.
|
downwards in the tree.
|
||||||
|
|
||||||
Always before processing a node,
|
Both in updates and queries,
|
||||||
the value of the lazy update is propagated
|
the value of a lazy update is always propagated
|
||||||
to the children of the node.
|
to the children of the node
|
||||||
This happens both in a range update
|
before processing the node.
|
||||||
and a range query.
|
The idea is that updates will be propagated
|
||||||
The idea is that the lazy update will be propagated
|
|
||||||
downwards only when it is necessary,
|
downwards only when it is necessary,
|
||||||
so 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 in the marked range:
|
when we calculate the sum of elements in $[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]$.
|
||||||
|
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\begin{tikzpicture}[scale=0.7]
|
\begin{tikzpicture}[scale=0.7]
|
||||||
\draw (0,0) grid (16,1);
|
\draw (0,0) grid (16,1);
|
||||||
|
@ -486,52 +494,53 @@ when we calculate the sum in the marked range:
|
||||||
\draw [decoration={brace}, decorate, line width=0.5mm] (14,-0.25) -- (10,-0.25);
|
\draw [decoration={brace}, decorate, line width=0.5mm] (14,-0.25) -- (10,-0.25);
|
||||||
|
|
||||||
\draw[color=blue,thick] (8,1.5) rectangle (12,5.5);
|
\draw[color=blue,thick] (8,1.5) rectangle (12,5.5);
|
||||||
|
|
||||||
|
\node at (10.5,-0.75) {$a$};
|
||||||
|
\node at (13.5,-0.75) {$b$};
|
||||||
\end{tikzpicture}
|
\end{tikzpicture}
|
||||||
\end{center}
|
\end{center}
|
||||||
The result of this query was that a lazy update was
|
|
||||||
propagated downwards in the nodes that are inside the rectangle.
|
|
||||||
It was necessary to propagate the lazy update,
|
|
||||||
because some of the updated elements were inside the range.
|
|
||||||
|
|
||||||
Note that sometimes it's necessary to combine lazy updates.
|
Note that sometimes it is needed to combine lazy updates.
|
||||||
This happens when a node already has a lazy update,
|
This happens when a node that already has a lazy update
|
||||||
and another lazy update will be added to it.
|
is assigned another lazy update.
|
||||||
In the above tree, it's easy to combine lazy updates
|
In this problem, it is easy to combine lazy updates,
|
||||||
because updates $z_1$ and $z_2$ combined equal to update $z_1+z_2$.
|
because the combination of updates $z_1$ and $z_2$
|
||||||
|
corresponds to an update $z_1+z_2$.
|
||||||
|
|
||||||
\subsubsection{Polynomial update}
|
\subsubsection{Polynomial update}
|
||||||
|
|
||||||
A lazy update can be generalized so that it's
|
Lazy updates can be generalized so that it is
|
||||||
allowed to update a range by a polynomial
|
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.\]
|
||||||
|
|
||||||
Here, the update for the first element in the range is $p(0)$,
|
In this case, the update for an element $i$
|
||||||
for the second element $p(1)$, etc., so the update
|
in the range $[a,b]$ is $p(i-a)$.
|
||||||
at index $i$ in range $[a,b]$ is $p(i-a)$.
|
For example, adding $p(u)=u+1$
|
||||||
For example, adding a polynomial $p(u)=u+1$
|
to $[a,b]$ means that the element at position $a$
|
||||||
to range $[a,b]$ means that the element at index $a$
|
is increased by 1, the element at position $a+1$
|
||||||
increases by 1, the element at index $a+1$
|
is increased by 2, etc.
|
||||||
increases by 2, etc.
|
|
||||||
|
|
||||||
A polynomial update can be supported by
|
To support polynomial updates,
|
||||||
storing $k+2$ values to each node where $k$
|
each node is assigned $k+2$ values,
|
||||||
equals the degree of the polynomial.
|
where $k$ equals the degree of the polynomial.
|
||||||
The value $s$ is the sum of the elements in the range,
|
The value $s$ is the sum of the elements in the range,
|
||||||
and 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 $[x,y]$ is
|
Now, the sum of elements 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.\]
|
||||||
that can be efficiently calculated using sum formulas
|
|
||||||
For example, the value $z_0$ corresponds to the sum
|
The value of such a sum
|
||||||
$(y-x+1)z_0$, and the value $z_1 u$ corresponds to the sum
|
can be efficiently calculated using sum formulas.
|
||||||
|
For example, the term $z_0$ corresponds to the sum
|
||||||
|
$(y-x+1)z_0$, and the term $z_1 u$ corresponds to the sum
|
||||||
\[z_1(0+1+\cdots+y-x) = z_1 \frac{(y-x)(y-x+1)}{2} .\]
|
\[z_1(0+1+\cdots+y-x) = z_1 \frac{(y-x)(y-x+1)}{2} .\]
|
||||||
|
|
||||||
When propagating an update in the tree,
|
When propagating an update in the tree,
|
||||||
the indices of the polynomial $p(u)$ change,
|
the indices of $p(u)$ change,
|
||||||
because in each range $[x,y]$,
|
because in each range $[x,y]$,
|
||||||
the values are
|
the values are
|
||||||
calculated for $x=0,1,\ldots,y-x$.
|
calculated for $u=0,1,\ldots,y-x$.
|
||||||
However, this is not a problem, because
|
However, this is not a problem, because
|
||||||
$p'(u)=p(u+h)$ is a polynomial
|
$p'(u)=p(u+h)$ is a polynomial
|
||||||
of equal degree as $p(u)$.
|
of equal degree as $p(u)$.
|
||||||
|
@ -542,17 +551,17 @@ For example, if $p(u)=t_2 u^2+t_1 u-t_0$, then
|
||||||
|
|
||||||
\index{dynamic segment tree}
|
\index{dynamic segment tree}
|
||||||
|
|
||||||
A regular segment tree is static,
|
An ordinary segment tree is static,
|
||||||
which means that each node has a fixed position
|
which means that each node has a fixed position
|
||||||
in the array and storing the tree requires
|
in the array and the tree requires
|
||||||
a fixed amount of memory.
|
a fixed amount of memory.
|
||||||
However, if most nodes are empty, such an
|
However, if most nodes are not used,
|
||||||
implementation wastes memory.
|
memory is wasted.
|
||||||
In a \key{dynamic segment tree},
|
In a \key{dynamic segment tree},
|
||||||
memory is reserved only for nodes that
|
memory is allocated only for nodes that
|
||||||
are actually needed.
|
are actually needed.
|
||||||
|
|
||||||
The nodes can be represented as structs as follows:
|
The nodes of a dynamic tree can be represented as structs:
|
||||||
|
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
struct node {
|
struct node {
|
||||||
|
@ -567,7 +576,7 @@ $[x,y]$ is the corresponding range,
|
||||||
and $l$ and $r$ point to the left
|
and $l$ and $r$ point to the left
|
||||||
and right subtree.
|
and right subtree.
|
||||||
|
|
||||||
After this, nodes can be manipulated as follows:
|
After this, nodes can be created as follows:
|
||||||
|
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
// create new node
|
// create new node
|
||||||
|
@ -581,19 +590,21 @@ u->s = 5;
|
||||||
\index{sparse segment tree}
|
\index{sparse segment tree}
|
||||||
|
|
||||||
A dynamic segment tree is useful if
|
A dynamic segment tree is useful if
|
||||||
the range $[0,N-1]$ covered by the tree is \emph{sparse},
|
the underlying array is \emph{sparse}.
|
||||||
which means that $N$ is large but only a
|
This means that the range $[0,N-1]$
|
||||||
small portion of the indices are used.
|
of allowed indices is large,
|
||||||
While a regular segment tree uses $O(n)$ memory,
|
but only a small portion of the indices are used
|
||||||
a dynamic segment tree only uses $O(n \log N)$ memory,
|
and most elements in the array are empty.
|
||||||
where $n$ is the number of indices used.
|
While an ordinary segment tree uses $O(N)$ memory,
|
||||||
|
a dynamic segment tree only requires $O(n \log N)$ memory,
|
||||||
|
where $n$ is the number of operations performed.
|
||||||
|
|
||||||
A \key{sparse segment tree} is initially empty
|
A \key{sparse segment tree} is initially empty
|
||||||
and its only node is $[0,N-1]$.
|
and its only node is $[0,N-1]$.
|
||||||
When the tree changes, new nodes are added dynamically
|
After updates, new nodes are added dynamically
|
||||||
always when they are needed because of new indices.
|
when needed.
|
||||||
For example, if $N=16$, and the elements
|
For example, if $N=16$ and the elements
|
||||||
in indices 3 and 10 have been changes,
|
in positions 3 and 10 have been modified,
|
||||||
the tree contains the following nodes:
|
the tree contains the following nodes:
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\begin{tikzpicture}[scale=0.9]
|
\begin{tikzpicture}[scale=0.9]
|
||||||
|
@ -620,17 +631,17 @@ the tree contains the following nodes:
|
||||||
\end{tikzpicture}
|
\end{tikzpicture}
|
||||||
\end{center}
|
\end{center}
|
||||||
|
|
||||||
Any path from the root to a leaf contains
|
Any path from the root node to a leaf contains
|
||||||
$O(\log N)$ nodes,
|
$O(\log N)$ nodes,
|
||||||
so each change 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$ changes, the tree contains
|
Thus, after $n$ operations, the tree contains
|
||||||
at most $O(n \log N)$ nodes.
|
at most $O(n \log N)$ nodes.
|
||||||
|
|
||||||
Note that if all indices of the elements
|
Note that if all indices of the elements
|
||||||
are known at the beginning of the algorithm,
|
are known at the beginning of the algorithm,
|
||||||
a dynamic segment tree is not needed,
|
a dynamic segment tree is not needed,
|
||||||
but we can use a regular segment tree with
|
but we can use an ordinary segment tree with
|
||||||
index compression (Chapter 9.4).
|
index compression (Chapter 9.4).
|
||||||
However, this is not possible if the indices
|
However, this is not possible if the indices
|
||||||
are generated during the algorithm.
|
are generated during the algorithm.
|
||||||
|
@ -638,26 +649,25 @@ are generated during the algorithm.
|
||||||
\subsubsection{Persistent segment tree}
|
\subsubsection{Persistent segment tree}
|
||||||
|
|
||||||
\index{persistent segment tree}
|
\index{persistent segment tree}
|
||||||
\index{version history}
|
|
||||||
|
|
||||||
Using a dynamic implementation,
|
Using a dynamic implementation,
|
||||||
it is also possible to create a
|
it is also possible to create a
|
||||||
\key{persistent segment tree} that stores
|
\key{persistent segment tree} that stores
|
||||||
the \key{version history} of the tree.
|
the \key{modification history} of the tree.
|
||||||
In such an implementation, we can
|
In such an implementation, we can
|
||||||
efficiently access
|
efficiently access
|
||||||
all versions of the tree that have been
|
all versions of the tree that have
|
||||||
existed during the algorithm.
|
existed during the algorithm.
|
||||||
|
|
||||||
When the version history is available,
|
When the modification history is available,
|
||||||
we can access all versions of the tree
|
we can perform queries in any previous tree
|
||||||
like a regular segment tree, because their
|
like in an ordinary segment tree, because the
|
||||||
structure is stored.
|
full structure of each tree is stored.
|
||||||
We can also derive new trees from the history
|
We can also create new trees based on previous
|
||||||
and further manipulate them.
|
trees and modify them independently.
|
||||||
|
|
||||||
Consider the following sequence of updates,
|
Consider the following sequence of updates,
|
||||||
where red nodes change in an update
|
where red nodes change
|
||||||
and other nodes remain the same:
|
and other nodes remain the same:
|
||||||
|
|
||||||
\begin{center}
|
\begin{center}
|
||||||
|
@ -711,10 +721,10 @@ and other nodes remain the same:
|
||||||
\end{center}
|
\end{center}
|
||||||
After each update, most nodes in the tree
|
After each update, most nodes in the tree
|
||||||
remain the same,
|
remain the same,
|
||||||
so an efficient way to store the version history
|
so an efficient way to store the modification history
|
||||||
is to represent each tree in the history as a combination
|
is to represent each tree in the history as a combination
|
||||||
of new nodes and subtrees of previous trees.
|
of new nodes and subtrees of previous trees.
|
||||||
In this case, the version history can be
|
In this example, the modification history can be
|
||||||
stored as follows:
|
stored as follows:
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\begin{tikzpicture}[scale=0.8]
|
\begin{tikzpicture}[scale=0.8]
|
||||||
|
@ -762,29 +772,28 @@ stored as follows:
|
||||||
\end{tikzpicture}
|
\end{tikzpicture}
|
||||||
\end{center}
|
\end{center}
|
||||||
|
|
||||||
The structure of each version of the tree in the history can be
|
The structure of each previous tree can be
|
||||||
reconstructed by following the pointers from the root.
|
reconstructed by following the pointers
|
||||||
Each update only adds $O(\log N)$ new nodes to the tree
|
starting at the corresponding root node.
|
||||||
when the indices are $[0,N-1]$,
|
Since each operation during the algorithm
|
||||||
so it is possible to store the full version history
|
adds only $O(\log N)$ new nodes to the tree,
|
||||||
of the tree.
|
it is possible to store the full modification history of the tree.
|
||||||
|
|
||||||
\section{Data structures}
|
\section{Data structures}
|
||||||
|
|
||||||
Insted of a single value, a node in a segment tree
|
Instead of single values, nodes in a segment tree
|
||||||
can also contain a data structure that maintains information
|
can also contain data structures that maintain information
|
||||||
about the corresponding range.
|
about the corresponding ranges.
|
||||||
In this case, the operations of the tree take
|
In such a tree, the operations take
|
||||||
$O(f(n) \log n)$ time, where $f(n)$ is
|
$O(f(n) \log n)$ time, where $f(n)$ is
|
||||||
the time needed for retrieving or updating the
|
the time needed for processing a single node during an operation.
|
||||||
information in a single node.
|
|
||||||
|
|
||||||
As an example, consider a segment tree that
|
As an example, consider a segment tree that
|
||||||
supports queries of the form
|
supports queries of the form
|
||||||
''how many times does element $x$ appear
|
''how many times does an element $x$ appear
|
||||||
in range $[a,b]$?''
|
in the range $[a,b]$?''
|
||||||
For example, element 1 appears three times
|
For example, the element 1 appears three times
|
||||||
in the following subarray:
|
in the following range:
|
||||||
|
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\begin{tikzpicture}[scale=0.7]
|
\begin{tikzpicture}[scale=0.7]
|
||||||
|
@ -802,12 +811,12 @@ in the following subarray:
|
||||||
\end{tikzpicture}
|
\end{tikzpicture}
|
||||||
\end{center}
|
\end{center}
|
||||||
|
|
||||||
The idea is to construct a segment tree
|
The idea is to build a segment tree
|
||||||
where each node has a data structure
|
where each node is assigned a data structure
|
||||||
that can return the number of any element $x$
|
that can calculate the number of any element $x$
|
||||||
in the range.
|
in the corresponding range.
|
||||||
Using such a segment tree,
|
Using such a segment tree,
|
||||||
the answer for a query can be calculated
|
the answer to a query can be calculated
|
||||||
by combining the results from the nodes
|
by combining the results from the nodes
|
||||||
that belong to the range.
|
that belong to the range.
|
||||||
|
|
||||||
|
@ -956,28 +965,25 @@ corresponds to the above array:
|
||||||
\end{tikzpicture}
|
\end{tikzpicture}
|
||||||
\end{center}
|
\end{center}
|
||||||
|
|
||||||
Each node in the tree should contain
|
For example, we can build the tree so
|
||||||
an appropriate data structure, for example a
|
that each node contains a \texttt{map} structure.
|
||||||
\texttt{map} structure.
|
In this case, the time needed for processing each
|
||||||
In this case, the time needed for accessing
|
node is $O(\log n)$, so the total time complexity
|
||||||
a node is $O(\log n)$, so the total time complexity
|
|
||||||
of a query is $O(\log^2 n)$.
|
of a query is $O(\log^2 n)$.
|
||||||
|
The tree uses $O(n \log n)$ memory,
|
||||||
Data structures in nodes increase the memory usage
|
because there are $O(\log n)$ levels
|
||||||
of the tree.
|
and each level contains
|
||||||
In this example, $O(n \log n)$ memory is needed,
|
$O(n)$ elements.
|
||||||
because the tree consists of $O(\log n)$ levels,
|
|
||||||
and the map structures contain a total
|
|
||||||
of $O(n)$ values at each level.
|
|
||||||
|
|
||||||
\section{Two-dimensionality}
|
\section{Two-dimensionality}
|
||||||
|
|
||||||
\index{two-dimensional segment tree}
|
\index{two-dimensional segment tree}
|
||||||
|
|
||||||
A \key{two-dimensional segment tree} supports
|
A \key{two-dimensional segment tree} supports
|
||||||
queries about rectangles in a two-dimensional array.
|
queries related to rectangular subarrays
|
||||||
Such a segment tree can be implemented as
|
of a two-dimensional array.
|
||||||
nested segmenet trees: a big tree corresponds to the
|
Such a tree can be implemented as
|
||||||
|
nested segment trees: a big tree corresponds to the
|
||||||
rows in the array, and each node contains a small tree
|
rows in the array, and each node contains a small tree
|
||||||
that corresponds to a column.
|
that corresponds to a column.
|
||||||
|
|
||||||
|
@ -1007,7 +1013,8 @@ For example, in the array
|
||||||
\node[anchor=center] at (3.5, 3.5) {6};
|
\node[anchor=center] at (3.5, 3.5) {6};
|
||||||
\end{tikzpicture}
|
\end{tikzpicture}
|
||||||
\end{center}
|
\end{center}
|
||||||
sums of rectangles can be calculated
|
the sum of any subarray
|
||||||
|
can be calculated
|
||||||
from the following segment tree:
|
from the following segment tree:
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\begin{tikzpicture}[scale=0.4]
|
\begin{tikzpicture}[scale=0.4]
|
||||||
|
@ -1156,10 +1163,6 @@ from the following segment tree:
|
||||||
|
|
||||||
The operations in a two-dimensional segment tree
|
The operations in a two-dimensional segment tree
|
||||||
take $O(\log^2 n)$ time, because the big tree
|
take $O(\log^2 n)$ time, because the big tree
|
||||||
and each small tree contain $O(\log n)$ levels.
|
and each small tree consist of $O(\log n)$ levels.
|
||||||
The tree uses $O(n^2)$ memory, because each
|
The tree requires $O(n^2)$ memory, because each
|
||||||
small tree uses $O(n)$ memory.
|
small tree contains $O(n)$ values.
|
||||||
|
|
||||||
Using a similar idea, it is also possible to create
|
|
||||||
segment trees with more dimensions,
|
|
||||||
but this is rarely needed.
|
|
||||||
|
|
Loading…
Reference in New Issue