2016-12-28 23:54:51 +01:00
|
|
|
\chapter{Square root algorithms}
|
|
|
|
|
2017-01-25 22:13:05 +01:00
|
|
|
\index{square root algorithm}
|
2016-12-28 23:54:51 +01:00
|
|
|
|
2017-01-25 22:13:05 +01:00
|
|
|
A \key{square root algorithm} is an algorithm
|
|
|
|
that has a square root in its time complexity.
|
|
|
|
A square root can be seen as a ''poor man's logarithm'':
|
|
|
|
the complexity $O(\sqrt n)$ is better than $O(n)$
|
|
|
|
but worse than $O(\log n)$.
|
2017-04-22 10:57:37 +02:00
|
|
|
In any case, many square root algorithms are fast and usable in practice.
|
2016-12-28 23:54:51 +01:00
|
|
|
|
2017-02-11 20:24:28 +01:00
|
|
|
As an example, let us consider the problem of
|
|
|
|
creating a data structure that supports
|
2017-02-18 17:46:37 +01:00
|
|
|
two operations on an array:
|
2017-02-11 20:24:28 +01:00
|
|
|
modifying an element at a given position
|
|
|
|
and calculating the sum of elements in the given range.
|
2017-01-25 22:13:05 +01:00
|
|
|
We have previously solved the problem using
|
2017-04-22 10:57:37 +02:00
|
|
|
binary indexed and segment trees,
|
2017-01-25 22:13:05 +01:00
|
|
|
that support both operations in $O(\log n)$ time.
|
|
|
|
However, now we will solve the problem
|
|
|
|
in another way using a square root structure
|
2017-02-11 20:24:28 +01:00
|
|
|
that allows us to modify elements in $O(1)$ time
|
|
|
|
and calculate sums in $O(\sqrt n)$ time.
|
2016-12-28 23:54:51 +01:00
|
|
|
|
2017-04-22 10:57:37 +02:00
|
|
|
The idea is to divide the array into \emph{blocks}
|
2017-01-26 22:12:30 +01:00
|
|
|
of size $\sqrt n$ so that each block contains
|
|
|
|
the sum of elements inside the block.
|
2017-02-11 20:24:28 +01:00
|
|
|
For example, an array of 16 elements will be
|
|
|
|
divided into blocks of 4 elements as follows:
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
|
|
\begin{center}
|
|
|
|
\begin{tikzpicture}[scale=0.7]
|
|
|
|
\draw (0,0) grid (16,1);
|
|
|
|
|
|
|
|
\draw (0,1) rectangle (4,2);
|
|
|
|
\draw (4,1) rectangle (8,2);
|
|
|
|
\draw (8,1) rectangle (12,2);
|
|
|
|
\draw (12,1) rectangle (16,2);
|
|
|
|
|
|
|
|
\node at (0.5, 0.5) {5};
|
|
|
|
\node at (1.5, 0.5) {8};
|
|
|
|
\node at (2.5, 0.5) {6};
|
|
|
|
\node at (3.5, 0.5) {3};
|
|
|
|
\node at (4.5, 0.5) {2};
|
|
|
|
\node at (5.5, 0.5) {7};
|
|
|
|
\node at (6.5, 0.5) {2};
|
|
|
|
\node at (7.5, 0.5) {6};
|
|
|
|
\node at (8.5, 0.5) {7};
|
|
|
|
\node at (9.5, 0.5) {1};
|
|
|
|
\node at (10.5, 0.5) {7};
|
|
|
|
\node at (11.5, 0.5) {5};
|
|
|
|
\node at (12.5, 0.5) {6};
|
|
|
|
\node at (13.5, 0.5) {2};
|
|
|
|
\node at (14.5, 0.5) {3};
|
|
|
|
\node at (15.5, 0.5) {2};
|
|
|
|
|
|
|
|
\node at (2, 1.5) {21};
|
|
|
|
\node at (6, 1.5) {17};
|
|
|
|
\node at (10, 1.5) {20};
|
|
|
|
\node at (14, 1.5) {13};
|
|
|
|
|
|
|
|
\end{tikzpicture}
|
|
|
|
\end{center}
|
|
|
|
|
2017-04-22 10:57:37 +02:00
|
|
|
In this structure,
|
|
|
|
it is easy to modify array elements,
|
2017-02-18 17:46:37 +01:00
|
|
|
because it is only needed to update
|
|
|
|
the sum of a single block
|
2017-02-11 20:24:28 +01:00
|
|
|
after each modification,
|
|
|
|
which can be done in $O(1)$ time.
|
|
|
|
For example, the following picture shows
|
|
|
|
how the value of an element and
|
|
|
|
the sum of the corresponding block change:
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
|
|
\begin{center}
|
|
|
|
\begin{tikzpicture}[scale=0.7]
|
|
|
|
\fill[color=lightgray] (5,0) rectangle (6,1);
|
|
|
|
\draw (0,0) grid (16,1);
|
|
|
|
|
|
|
|
\fill[color=lightgray] (4,1) rectangle (8,2);
|
|
|
|
\draw (0,1) rectangle (4,2);
|
|
|
|
\draw (4,1) rectangle (8,2);
|
|
|
|
\draw (8,1) rectangle (12,2);
|
|
|
|
\draw (12,1) rectangle (16,2);
|
|
|
|
|
|
|
|
\node at (0.5, 0.5) {5};
|
|
|
|
\node at (1.5, 0.5) {8};
|
|
|
|
\node at (2.5, 0.5) {6};
|
|
|
|
\node at (3.5, 0.5) {3};
|
|
|
|
\node at (4.5, 0.5) {2};
|
|
|
|
\node at (5.5, 0.5) {5};
|
|
|
|
\node at (6.5, 0.5) {2};
|
|
|
|
\node at (7.5, 0.5) {6};
|
|
|
|
\node at (8.5, 0.5) {7};
|
|
|
|
\node at (9.5, 0.5) {1};
|
|
|
|
\node at (10.5, 0.5) {7};
|
|
|
|
\node at (11.5, 0.5) {5};
|
|
|
|
\node at (12.5, 0.5) {6};
|
|
|
|
\node at (13.5, 0.5) {2};
|
|
|
|
\node at (14.5, 0.5) {3};
|
|
|
|
\node at (15.5, 0.5) {2};
|
|
|
|
|
|
|
|
\node at (2, 1.5) {21};
|
|
|
|
\node at (6, 1.5) {15};
|
|
|
|
\node at (10, 1.5) {20};
|
|
|
|
\node at (14, 1.5) {13};
|
|
|
|
|
|
|
|
\end{tikzpicture}
|
|
|
|
\end{center}
|
|
|
|
|
2017-02-11 20:24:28 +01:00
|
|
|
Calculating the sum of elements in a range is
|
|
|
|
a bit more difficult.
|
|
|
|
It turns out that we can always divide
|
|
|
|
the range into three parts such that
|
|
|
|
the sum consists of values of single elements
|
|
|
|
and sums of blocks between them:
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
|
|
\begin{center}
|
|
|
|
\begin{tikzpicture}[scale=0.7]
|
|
|
|
\fill[color=lightgray] (3,0) rectangle (4,1);
|
|
|
|
\fill[color=lightgray] (12,0) rectangle (13,1);
|
|
|
|
\fill[color=lightgray] (13,0) rectangle (14,1);
|
|
|
|
\draw (0,0) grid (16,1);
|
|
|
|
|
|
|
|
\fill[color=lightgray] (4,1) rectangle (8,2);
|
|
|
|
\fill[color=lightgray] (8,1) rectangle (12,2);
|
|
|
|
\draw (0,1) rectangle (4,2);
|
|
|
|
\draw (4,1) rectangle (8,2);
|
|
|
|
\draw (8,1) rectangle (12,2);
|
|
|
|
\draw (12,1) rectangle (16,2);
|
|
|
|
|
|
|
|
\node at (0.5, 0.5) {5};
|
|
|
|
\node at (1.5, 0.5) {8};
|
|
|
|
\node at (2.5, 0.5) {6};
|
|
|
|
\node at (3.5, 0.5) {3};
|
|
|
|
\node at (4.5, 0.5) {2};
|
|
|
|
\node at (5.5, 0.5) {5};
|
|
|
|
\node at (6.5, 0.5) {2};
|
|
|
|
\node at (7.5, 0.5) {6};
|
|
|
|
\node at (8.5, 0.5) {7};
|
|
|
|
\node at (9.5, 0.5) {1};
|
|
|
|
\node at (10.5, 0.5) {7};
|
|
|
|
\node at (11.5, 0.5) {5};
|
|
|
|
\node at (12.5, 0.5) {6};
|
|
|
|
\node at (13.5, 0.5) {2};
|
|
|
|
\node at (14.5, 0.5) {3};
|
|
|
|
\node at (15.5, 0.5) {2};
|
|
|
|
|
|
|
|
\node at (2, 1.5) {21};
|
|
|
|
\node at (6, 1.5) {15};
|
|
|
|
\node at (10, 1.5) {20};
|
|
|
|
\node at (14, 1.5) {13};
|
|
|
|
|
|
|
|
\draw [decoration={brace}, decorate, line width=0.5mm] (14,-0.25) -- (3,-0.25);
|
|
|
|
|
|
|
|
\end{tikzpicture}
|
|
|
|
\end{center}
|
|
|
|
|
2017-02-11 20:24:28 +01:00
|
|
|
Since the number of single elements is $O(\sqrt n)$
|
2017-04-22 10:57:37 +02:00
|
|
|
and the number of blocks is also $O(\sqrt n)$,
|
2017-02-11 20:24:28 +01:00
|
|
|
the time complexity of the sum query is $O(\sqrt n)$.
|
2017-04-22 10:57:37 +02:00
|
|
|
In this case, the parameter $\sqrt n$ balances two things:
|
2017-02-11 20:24:28 +01:00
|
|
|
the array is divided into $\sqrt n$ blocks,
|
|
|
|
each of which contains $\sqrt n$ elements.
|
2016-12-28 23:54:51 +01:00
|
|
|
|
2017-02-11 20:24:28 +01:00
|
|
|
In practice, it is not needed to use the
|
2017-02-18 17:46:37 +01:00
|
|
|
exact value of $\sqrt n$ as a parameter, but it may be better to
|
2017-01-25 22:13:05 +01:00
|
|
|
use parameters $k$ and $n/k$ where $k$ is
|
2017-02-18 17:46:37 +01:00
|
|
|
different from $\sqrt n$.
|
2017-02-11 20:24:28 +01:00
|
|
|
The optimal parameter depends on the problem and input.
|
|
|
|
For example, if an algorithm often goes
|
|
|
|
through the blocks but rarely inspects
|
|
|
|
single elements inside the blocks,
|
|
|
|
it may be a good idea to divide the array into
|
2017-01-26 22:12:30 +01:00
|
|
|
$k < \sqrt n$ blocks, each of which contains $n/k > \sqrt n$
|
2017-01-25 22:13:05 +01:00
|
|
|
elements.
|
|
|
|
|
|
|
|
\section{Batch processing}
|
|
|
|
|
|
|
|
\index{batch processing}
|
|
|
|
|
2017-02-11 20:24:28 +01:00
|
|
|
Sometimes the operations of an algorithm
|
2017-04-22 10:57:37 +02:00
|
|
|
can be divided into \emph{batches}.
|
|
|
|
Each batch contains a sequence of operations
|
|
|
|
which will be processed one after another.
|
2017-02-11 20:24:28 +01:00
|
|
|
Some precalculation is done
|
|
|
|
between the batches
|
|
|
|
in order to process the future operations more efficiently.
|
|
|
|
If there are $O(\sqrt n)$ batches of size $O(\sqrt n)$,
|
|
|
|
this results in a square root algorithm.
|
|
|
|
|
|
|
|
As an example, let us consider a problem
|
2017-01-25 22:13:05 +01:00
|
|
|
where a grid of size $k \times k$
|
2017-02-18 17:46:37 +01:00
|
|
|
initially consists of white squares
|
2017-02-11 20:24:28 +01:00
|
|
|
and our task is to perform $n$ operations,
|
2017-01-25 22:13:05 +01:00
|
|
|
each of which is one of the following:
|
2016-12-28 23:54:51 +01:00
|
|
|
\begin{itemize}
|
|
|
|
\item
|
2017-01-25 22:13:05 +01:00
|
|
|
paint square $(y,x)$ black
|
2016-12-28 23:54:51 +01:00
|
|
|
\item
|
2017-01-25 22:13:05 +01:00
|
|
|
find the nearest black square to
|
|
|
|
square $(y,x)$ where the distance
|
|
|
|
between squares $(y_1,x_1)$ and $(y_2,x_2)$
|
|
|
|
is $|y_1-y_2|+|x_1-x_2|$
|
2016-12-28 23:54:51 +01:00
|
|
|
\end{itemize}
|
|
|
|
|
2017-02-11 20:24:28 +01:00
|
|
|
We can solve the problem by dividing
|
|
|
|
the operations into
|
2017-01-25 22:13:05 +01:00
|
|
|
$O(\sqrt n)$ batches, each of which consists
|
|
|
|
of $O(\sqrt n)$ operations.
|
|
|
|
At the beginning of each batch,
|
2017-02-18 17:46:37 +01:00
|
|
|
we calculate for each square of the grid
|
2017-01-25 22:13:05 +01:00
|
|
|
the smallest distance to a black square.
|
|
|
|
This can be done in $O(k^2)$ time using breadth-first search.
|
|
|
|
|
|
|
|
When processing a batch, we maintain a list of squares
|
|
|
|
that have been painted black in the current batch.
|
2017-02-11 20:24:28 +01:00
|
|
|
The list contains $O(\sqrt n)$ elements,
|
|
|
|
because there are $O(\sqrt n)$ operations in each batch.
|
2017-02-18 17:46:37 +01:00
|
|
|
Now, the distance from a square to the nearest black
|
2017-01-25 22:13:05 +01:00
|
|
|
square is either the precalculated distance or the distance
|
2017-02-18 17:46:37 +01:00
|
|
|
to a square that appears in the list.
|
2017-01-25 22:13:05 +01:00
|
|
|
|
|
|
|
The algorithm works in
|
|
|
|
$O((k^2+n) \sqrt n)$ time.
|
2017-02-11 20:24:28 +01:00
|
|
|
First, there are $O(\sqrt n)$ breadth-first searches
|
|
|
|
and each search takes $O(k^2)$ time.
|
|
|
|
Second, the total number of
|
2017-04-22 10:57:37 +02:00
|
|
|
distances calculated during the algorithm
|
|
|
|
is $O(n)$, and when calculating each distance,
|
2017-02-11 20:24:28 +01:00
|
|
|
we go through a list of $O(\sqrt n)$ squares.
|
2017-01-25 22:13:05 +01:00
|
|
|
|
|
|
|
If the algorithm would perform a breadth-first search
|
2017-02-11 20:24:28 +01:00
|
|
|
at each operation, the time complexity would be
|
2017-01-25 22:13:05 +01:00
|
|
|
$O(k^2 n)$.
|
|
|
|
And if the algorithm would go through all painted
|
|
|
|
squares at each operation,
|
2017-02-11 20:24:28 +01:00
|
|
|
the time complexity would be $O(n^2)$.
|
|
|
|
Thus, the time complexity of the square root algorithm
|
|
|
|
is a combination of these time complexities,
|
2017-02-18 17:46:37 +01:00
|
|
|
but in addition, a factor of $n$ is replaced by $\sqrt n$.
|
2017-02-11 20:24:28 +01:00
|
|
|
|
|
|
|
\section{Subalgorithms}
|
|
|
|
|
2017-04-22 10:57:37 +02:00
|
|
|
Some square root algorithms consist of
|
|
|
|
\emph{subalgorithms} that are specialized for different
|
2017-02-11 20:24:28 +01:00
|
|
|
input parameters.
|
|
|
|
Typically, there are two subalgorithms:
|
|
|
|
one algorithm is efficient when
|
|
|
|
some parameter is smaller than $\sqrt n$,
|
|
|
|
and another algorithm is efficient
|
|
|
|
when the parameter is larger than $\sqrt n$.
|
|
|
|
|
|
|
|
As an example, let us consider a problem where
|
|
|
|
we are given a tree of $n$ nodes,
|
2017-01-25 22:13:05 +01:00
|
|
|
each with some color. Our task is to find two nodes
|
2017-02-11 20:24:28 +01:00
|
|
|
that have the same color and whose distance
|
|
|
|
is as large as possible.
|
2017-01-25 22:13:05 +01:00
|
|
|
|
2017-02-11 20:24:28 +01:00
|
|
|
For example, in the following tree,
|
|
|
|
the maximum distance is 4 between
|
|
|
|
the red nodes 3 and 4:
|
2017-01-25 22:13:05 +01:00
|
|
|
|
2017-02-11 20:24:28 +01:00
|
|
|
\begin{center}
|
|
|
|
\begin{tikzpicture}[scale=0.9]
|
|
|
|
\node[draw, circle, fill=green!40] (1) at (1,3) {$2$};
|
|
|
|
\node[draw, circle, fill=red!40] (2) at (4,3) {$3$};
|
|
|
|
\node[draw, circle, fill=red!40] (3) at (1,1) {$5$};
|
|
|
|
\node[draw, circle, fill=blue!40] (4) at (4,1) {$6$};
|
|
|
|
\node[draw, circle, fill=red!40] (5) at (-2,1) {$4$};
|
|
|
|
\node[draw, circle, fill=blue!40] (6) at (-2,3) {$1$};
|
|
|
|
\path[draw,thick,-] (1) -- (2);
|
|
|
|
\path[draw,thick,-] (1) -- (3);
|
|
|
|
\path[draw,thick,-] (3) -- (4);
|
|
|
|
\path[draw,thick,-] (3) -- (6);
|
|
|
|
\path[draw,thick,-] (5) -- (6);
|
|
|
|
\end{tikzpicture}
|
|
|
|
\end{center}
|
2017-01-25 22:13:05 +01:00
|
|
|
|
2017-02-11 20:24:28 +01:00
|
|
|
The problem can be solved by going through
|
|
|
|
all colors and calculating
|
2017-02-18 17:46:37 +01:00
|
|
|
the maximum distance between two nodes
|
|
|
|
separately for each color.
|
2017-02-11 20:24:28 +01:00
|
|
|
Assume that the current color is $x$ and
|
|
|
|
there are $c$ nodes whose color is $x$.
|
|
|
|
There are two subalgorithms
|
|
|
|
that are specialized for small and large
|
|
|
|
values of $c$:
|
|
|
|
|
|
|
|
\emph{Case 1}: $c \le \sqrt n$.
|
2017-01-25 22:13:05 +01:00
|
|
|
If the number of nodes is small,
|
|
|
|
we go through all pairs of nodes whose
|
|
|
|
color is $x$ and select the pair that
|
|
|
|
has the maximum distance.
|
2017-02-11 20:24:28 +01:00
|
|
|
For each node, it is needed to calculate the distance
|
2017-02-11 20:27:03 +01:00
|
|
|
to $O(\sqrt n)$ other nodes (see Chapter 18.3),
|
2017-01-25 22:13:05 +01:00
|
|
|
so the total time needed for processing all
|
2017-02-11 20:24:28 +01:00
|
|
|
nodes is $O(n \sqrt n)$.
|
2017-01-25 22:13:05 +01:00
|
|
|
|
2017-02-11 20:24:28 +01:00
|
|
|
\emph{Case 2}: $c > \sqrt n$.
|
2017-01-25 22:13:05 +01:00
|
|
|
If the number of nodes is large,
|
2017-02-11 20:24:28 +01:00
|
|
|
we go through the whole tree
|
2017-01-25 22:13:05 +01:00
|
|
|
and calculate the maximum distance between
|
|
|
|
two nodes with color $x$.
|
|
|
|
The time complexity of the tree traversal is $O(n)$,
|
|
|
|
and this will be done at most $O(\sqrt n)$ times,
|
2017-02-11 20:24:28 +01:00
|
|
|
so the total time needed is $O(n \sqrt n)$.
|
|
|
|
|
2017-01-25 22:13:05 +01:00
|
|
|
The time complexity of the algorithm is $O(n \sqrt n)$,
|
2017-02-18 17:46:37 +01:00
|
|
|
because both cases take a total of $O(n \sqrt n)$ time.
|
2017-01-25 22:13:05 +01:00
|
|
|
|
|
|
|
\section{Mo's algorithm}
|
|
|
|
|
|
|
|
\index{Mo's algorithm}
|
|
|
|
|
2017-02-22 20:53:11 +01:00
|
|
|
\key{Mo's algorithm}\footnote{According to \cite{cod15}, this algorithm
|
|
|
|
is named after Mo Tao, a Chinese competitive programmer, but
|
2017-04-22 11:00:43 +02:00
|
|
|
the technique has appeared earlier in the literature \cite{ken06}.}
|
|
|
|
can be used in many problems
|
2017-01-26 22:12:30 +01:00
|
|
|
that require processing range queries in
|
2017-01-25 22:13:05 +01:00
|
|
|
a \emph{static} array.
|
2017-04-22 10:57:37 +02:00
|
|
|
Since the array is static, the queries can be
|
|
|
|
processed in any order.
|
2017-01-26 22:12:30 +01:00
|
|
|
Before processing the queries, the algorithm
|
|
|
|
sorts them in a special order which guarantees
|
2017-02-11 20:24:28 +01:00
|
|
|
that the algorithm works efficiently.
|
2017-01-26 22:12:30 +01:00
|
|
|
|
|
|
|
At each moment in the algorithm, there is an active
|
2017-02-11 20:24:28 +01:00
|
|
|
range and the algorithm maintains the answer
|
|
|
|
to a query related to that range.
|
|
|
|
The algorithm processes the queries one by one,
|
2017-02-18 17:46:37 +01:00
|
|
|
and always moves the endpoints of the
|
2017-02-11 20:24:28 +01:00
|
|
|
active range by inserting and removing elements.
|
2017-01-25 22:13:05 +01:00
|
|
|
The time complexity of the algorithm is
|
2017-02-18 17:46:37 +01:00
|
|
|
$O(n \sqrt n f(n))$ when the array contains
|
|
|
|
$n$ elements, there are $n$ queries
|
2017-01-26 22:12:30 +01:00
|
|
|
and each insertion and removal of an element
|
|
|
|
takes $O(f(n))$ time.
|
|
|
|
|
2017-02-11 20:24:28 +01:00
|
|
|
The trick in Mo's algorithm is the order
|
|
|
|
in which the queries are processed:
|
|
|
|
The array is divided into blocks of $O(\sqrt n)$
|
2017-01-26 22:12:30 +01:00
|
|
|
elements, and the queries are sorted primarily by
|
2017-02-11 20:24:28 +01:00
|
|
|
the number of the block that contains the first element
|
|
|
|
in the range, and secondarily by the position of the
|
|
|
|
last element in the range.
|
2017-01-25 22:13:05 +01:00
|
|
|
It turns out that using this order, the algorithm
|
2017-01-26 22:12:30 +01:00
|
|
|
only performs $O(n \sqrt n)$ operations,
|
2017-04-22 10:57:37 +02:00
|
|
|
because the left endpoint moves
|
2017-01-26 22:12:30 +01:00
|
|
|
$n$ times $O(\sqrt n)$ steps,
|
2017-04-22 10:57:37 +02:00
|
|
|
and the right endpoint moves
|
2017-02-11 20:24:28 +01:00
|
|
|
$\sqrt n$ times $O(n)$ steps. Thus, both
|
|
|
|
endpoints move a total of $O(n \sqrt n)$ steps during the algorithm.
|
2017-01-25 22:13:05 +01:00
|
|
|
|
|
|
|
\subsubsection*{Example}
|
|
|
|
|
2017-02-11 20:24:28 +01:00
|
|
|
As an example, consider a problem
|
|
|
|
where we are given a set of queries,
|
|
|
|
each of them corresponding to a range in an array,
|
|
|
|
and our task is to calculate for each query
|
2017-02-18 17:46:37 +01:00
|
|
|
the number of \emph{distinct} elements in the range.
|
2017-01-25 22:13:05 +01:00
|
|
|
|
|
|
|
In Mo's algorithm, the queries are always sorted
|
2017-02-11 20:24:28 +01:00
|
|
|
in the same way, but it depends on the problem
|
|
|
|
how the answer to the query is maintained.
|
2017-01-25 22:13:05 +01:00
|
|
|
In this problem, we can maintain an array
|
2017-04-22 10:57:37 +02:00
|
|
|
\texttt{count} where $\texttt{count}[x]$
|
2017-02-18 17:46:37 +01:00
|
|
|
indicates the number of times an element $x$
|
2017-02-11 20:24:28 +01:00
|
|
|
occurs in the active range.
|
2017-01-25 22:13:05 +01:00
|
|
|
|
2017-02-11 20:24:28 +01:00
|
|
|
When we move from one query to another query,
|
|
|
|
the active range changes.
|
|
|
|
For example, if the current range is
|
2016-12-28 23:54:51 +01:00
|
|
|
\begin{center}
|
|
|
|
\begin{tikzpicture}[scale=0.7]
|
|
|
|
\fill[color=lightgray] (1,0) rectangle (5,1);
|
|
|
|
\draw (0,0) grid (9,1);
|
|
|
|
\node at (0.5, 0.5) {4};
|
|
|
|
\node at (1.5, 0.5) {2};
|
|
|
|
\node at (2.5, 0.5) {5};
|
|
|
|
\node at (3.5, 0.5) {4};
|
|
|
|
\node at (4.5, 0.5) {2};
|
|
|
|
\node at (5.5, 0.5) {4};
|
|
|
|
\node at (6.5, 0.5) {3};
|
|
|
|
\node at (7.5, 0.5) {3};
|
|
|
|
\node at (8.5, 0.5) {4};
|
|
|
|
\end{tikzpicture}
|
|
|
|
\end{center}
|
2017-02-11 20:24:28 +01:00
|
|
|
and the next range is
|
2016-12-28 23:54:51 +01:00
|
|
|
\begin{center}
|
|
|
|
\begin{tikzpicture}[scale=0.7]
|
|
|
|
\fill[color=lightgray] (2,0) rectangle (7,1);
|
|
|
|
\draw (0,0) grid (9,1);
|
|
|
|
\node at (0.5, 0.5) {4};
|
|
|
|
\node at (1.5, 0.5) {2};
|
|
|
|
\node at (2.5, 0.5) {5};
|
|
|
|
\node at (3.5, 0.5) {4};
|
|
|
|
\node at (4.5, 0.5) {2};
|
|
|
|
\node at (5.5, 0.5) {4};
|
|
|
|
\node at (6.5, 0.5) {3};
|
|
|
|
\node at (7.5, 0.5) {3};
|
|
|
|
\node at (8.5, 0.5) {4};
|
|
|
|
\end{tikzpicture}
|
|
|
|
\end{center}
|
2017-01-25 22:13:05 +01:00
|
|
|
there will be three steps:
|
2017-04-17 11:18:29 +02:00
|
|
|
the left endpoint moves one step to the right,
|
2017-02-11 20:24:28 +01:00
|
|
|
and the right endpoint moves two steps to the right.
|
2017-01-25 22:13:05 +01:00
|
|
|
|
2017-04-22 10:57:37 +02:00
|
|
|
After each step, the array \texttt{count}
|
2017-02-11 20:38:13 +01:00
|
|
|
needs to be updated.
|
2017-02-11 20:31:56 +01:00
|
|
|
After adding an element $x$,
|
|
|
|
we increase the value of
|
2017-04-22 10:57:37 +02:00
|
|
|
$\texttt{count}[x]$ by 1,
|
|
|
|
and if $\texttt{count}[x]=1$ after this,
|
|
|
|
we also increase the answer to the query by 1.
|
2017-02-11 20:38:13 +01:00
|
|
|
Similarly, after removing an element $x$,
|
2017-02-11 20:31:56 +01:00
|
|
|
we decrease the value of
|
2017-04-22 10:57:37 +02:00
|
|
|
$\texttt{count}[x]$ by 1,
|
|
|
|
and if $\texttt{count}[x]=0$ after this,
|
|
|
|
we also decrease the answer to the query by 1.
|
2017-01-25 22:13:05 +01:00
|
|
|
|
|
|
|
In this problem, the time needed to perform
|
|
|
|
each step is $O(1)$, so the total time complexity
|
2017-01-26 22:12:30 +01:00
|
|
|
of the algorithm is $O(n \sqrt n)$.
|