Codeforces Round 1133 (Div. 1, Div. 2) is coming soon! Register here. ×

Sersp3's blog

By Sersp3, history, 2 months ago, In English

I couldn't find this particular approach in existing editorials, so I decided to share it.

1. Problem

You are given a 2D table $$$a$$$ with $$$n$$$ rows and $$$m$$$ columns. You need to answer $$$q$$$ queries offline. Each query is four integers $$$x_1$$$ $$$y_1$$$ $$$x_2$$$ $$$y_2$$$ describing a rectangle. For each query, you need to output

$$$\sum\limits_{i=x_1}^{x_2} (\max\limits_{j=y_1}^{y_2} a_{i, j})$$$

2. How to Approach the Solution

If we calculate the maximum for each row separately for every query, we get $$$O(qmn)$$$. We can build a Sparse Table for each row and find each maximum in $$$O(1)$$$. This reduces the total complexity to $$$O(mn\log(m) + qn)$$$. However, we can do better.

3. Removing Maximums from the Query

Let's create new table $$$b$$$ with $$$n$$$ rows and $$$m$$$ columns.

$$$ b_{i,j}= \begin{cases} \max\limits_{k=0}^{j}(a_{i,k}) - \max\limits_{k=0}^{j-1}(a_{i,k}), & j \ne 0,\\ a_{i,j}, & j = 0. \end{cases} $$$

Now, if a query has $$$y_1 = 0$$$, its answer is simply

$$$ \sum\limits_{i=x_1}^{x_2}\sum\limits_{j=y_1}^{y_2} b_{i,j}. $$$

Indeed, because

$$$ \forall i \in [0,n-1],\quad \sum\limits_{j=0}^{y} b_{i,j} = \max\limits_{k=0}^{y} a_{i,k}. $$$

4. Moving the Left Border

The main problem now is updating $$$b_{i, j}$$$ when we move the left border from $$$y_1$$$ to $$$y_1+1$$$. Let's consider one row separately and assume $$$y_1 = 0$$$, as the other cases are similar. Now, let $$$a$$$ be a one-dimensional array. Let $$$c$$$ denote the array $$$b$$$ after moving the left border from $$$0$$$ to $$$1$$$.

$$$ b_i = \begin{cases} a_i, & i = 0,\\ \max\limits_{k=0}^{i} a_k - \max\limits_{k=0}^{i-1} a_k, & i \gt 0. \end{cases} $$$
$$$ c_i = \begin{cases} 0, & i \lt 1,\\ a_i, & i = 1,\\ \max\limits_{k=1}^{i} a_k - \max\limits_{k=1}^{i-1} a_k, & i \gt 1. \end{cases} $$$

Let's define a one-dimensional array $$$right$$$, where $$$right_i$$$ is the index of the nearest element to the right that is greater than $$$a_i$$$. We can calculate the $$$right$$$ array using a monotonic stack. Let's consider two cases: $$$a_1 \ge a_0$$$ and $$$a_1 \lt a_0$$$ The first case is easy. If $$$a_1 \ge a_0$$$, we only need to change two values in $$$b$$$:

$$$ b_0 \leftarrow 0,\qquad b_1 \leftarrow a_1. $$$

The second case is harder. If $$$a_1 \lt a_0$$$

$$$ b_1 \leftarrow 0, $$$
$$$ b_{\text{right}_i} \leftarrow a_{\text{right}_i} - a_i, \qquad i = 1,\ \text{right}_1,\ \text{right}_{\text{right}_1},\ldots $$$

while

$$$ a_i \lt a_0. $$$

Indeed, after removing $$$a_0$$$, the prefix maximum remains equal to $$$a_i$$$ until we reach the first element greater than $$$a_i$$$. Therefore, for every element $$$a_i \lt a_0$$$ on this chain, its next greater element $$$a_{right_i}$$$ becomes the position where the prefix maximum increases, so its new contribution is $$$a_{right_i} - a_i$$$.

Once we reach an element $$$a_i \gt a_0$$$, all further values of $$$b$$$ remain unchanged, since $$$a_0$$$ was not the maximum anymore.

Thus, we can update the representation for the new left border by following the chain of nearest greater elements.

5. Processing the Queries

We can now process all queries offline by sorting them by their left border $$$y_1$$$.

Initially, we set $$$y_1=0$$$.

and construct the corresponding array $$$b$$$. Then we process the queries in increasing order of $$$y_1$$$. Whenever we move the current left border from $$$y_1$$$ to $$$y_1+1$$$, we apply the updates described in the previous section to every row.

After the current left border becomes equal to the left border of the query, the answer can be obtained as

$$$ \sum\limits_{i=x_1}^{x_2}\sum\limits_{j=y_1}^{y_2} b_{i,j}. $$$

Therefore, we need a data structure that supports point updates of $$$b_{i,j}$$$ and rectangle sum queries.

We can use a two-dimensional Fenwick tree for this purpose. Every change of one value of $$$b$$$ is performed as a point update, while the answer to a query is obtained using four prefix sums.

Thus, after sorting the queries by $$$y_1$$$, we maintain the correct representation of the prefix maximums for the current left border and answer every query independently.

6. Complexity of Updating b

The only remaining question is how many changes we perform when moving the left border.

Consider one row. When the left border is moved from $$$y_1$$$ to $$$y_1+1$$$, we follow the chain of nearest greater elements

$$$ i=y_1+1,\quad i=\text{right}_{i},\quad i=\text{right}_{i},\ldots $$$

while

$$$ a_i \lt a_{y_1}. $$$

So we need to take the contribution of these chains into account when analyzing the complexity. Let's prove that any two chains are disjoint. This will give us a total complexity of $$$O(m)$$$.

Let $$$i$$$ and $$$j$$$ be the starting points of two chains, and assume that $$$i \lt j$$$. Suppose, for contradiction, that $$$k$$$ is a common element of the two chains.

Since $$$k$$$ belongs to the chain starting at $$$i$$$, we have $$$a_i \lt a_k$$$.

On the other hand, $$$k$$$ belongs to the chain starting at $$$j$$$. By the definition of the chain, it is followed while $$$a_k \lt a_{j-1}$$$. Therefore, $$$a_{j-1} \gt a_k$$$.

But since $$$i \lt j$$$, the element $$$a_k$$$ is a maximum of the segment $$$[i,k]$$$, which contains $$$a_{j-1}$$$. Therefore, $$$a_k \ge a_{j-1}$$$ which contradicts $$$a_{j-1} \gt a_k$$$.

Since all elements of the chains are indices in the range $$$[0,m-1]$$$, and the chains are pairwise disjoint, the total number of updated indices in $$$b$$$ is $$$O(m)$$$.

7. Complexity

Let's analyze the complexity.

For every row, moving the left border requires following a chain of nearest greater elements. Each element can be processed only when it is reached by this procedure, so the total number of such updates over the whole process is linear in the number of elements of the row.

As shown in the previous section, the total number of updates to $$$b$$$ is

$$$ O(nm). $$$

Using a two-dimensional Fenwick tree, each point update and each prefix-sum query takes

$$$ O(\log n\log m). $$$

Hence, maintaining $$$b$$$ takes

$$$ O(nm\log n\log m). $$$

Each rectangle query requires four prefix sums, so all $$$q$$$ queries take

$$$ O(q\log n\log m). $$$

The total complexity is therefore

$$$ \boxed{ O(nm\log n\log m + q(\log q + \log n\log m)) }. $$$

The memory complexity is

$$$ \boxed{O(nm+q)}. $$$

Update: This approach can also be extended to online queries using a Fenwick tree over the rows with a persistent segment tree inside each node. We can keep a separate version for every possible left border $$$y_1$$$, allowing queries to be answered in arbitrary order.

8. Usage

This problem can be solved using this idea. Moreover, the same idea allows us to simplify the solution to a one-dimensional Fenwick tree.

This solution achieves a better time complexity than the one presented in the editorial.

  • Vote: I like it
  • +71
  • Vote: I do not like it

»
2 months ago, hide # |
 
Vote: I like it 0 Vote: I do not like it

Auto comment: topic has been updated by Sersp3 (previous revision, new revision, compare).

»
2 months ago, hide # |
 
Vote: I like it 0 Vote: I do not like it

nice

»
2 months ago, hide # |
← Rev. 2  
Vote: I like it +11 Vote: I do not like it

Omg, I haven't read anything this amazing and beautiful for a long time. Respect for your amortized analysis. Also probably you can answer queries online with persistent segtree

»
2 months ago, hide # |
 
Vote: I like it 0 Vote: I do not like it

Cool

»
2 months ago, hide # |
 
Vote: I like it 0 Vote: I do not like it

Auto comment: topic has been updated by Sersp3 (previous revision, new revision, compare).

»
2 months ago, hide # |
 
Vote: I like it 0 Vote: I do not like it

Love this kind of blogs, this one in particular.

orz

»
2 months ago, hide # |
 
Vote: I like it +6 Vote: I do not like it

Nice blog

just few polishing notes on the blog

\sum\limits_{i=x_1}^{x_2} (\max_{j=y_1}^{y_2} a_{i, j})

to get $$$\sum\limits_{i=x_1}^{x_2} (\max\limits_{j=y_1}^{y_2} a_{i, j})$$$


b_{\text{right}_i} \leftarrow a_{\text{right}_i} - a_i, \qquad i = 1,\ \text{right}_1,\ \text{right}_{\text{right}_1},\ldots

to get $$$b_{\text{right}_i} \leftarrow a_{\text{right}_i} - a_i, \qquad i = 1,\ \text{right}_1,\ \text{right}_{\text{right}_1},\ldots$$$


\sum\limits_{i=x_1}^{x_2}\sum\limits_{j=y_1}^{y_2} b_{i,j}.

To get $$$\sum\limits_{i=x_1}^{x_2}\sum\limits_{j=y_1}^{y_2} b_{i,j}.$$$

(use \limits whenever you've an inline \sum , it looks better :) )

  • »
    »
    2 months ago, hide # ^ |
     
    Vote: I like it +3 Vote: I do not like it

    Thanks a lot! My LaTeX isn't very good yet, so I really appreciate the help :)

»
2 months ago, hide # |
 
Vote: I like it +3 Vote: I do not like it

Auto comment: topic has been updated by Sersp3 (previous revision, new revision, compare).