Sparse Tables#691

Sparse Tables (Editorial)

The problem requires repeatedly finding the minimum on a segment of an array that does not change after generation. The number of queries reaches 10^7, so each query must be processed in . To do this, we use a sparse table — a static data structure for the RMQ (Range Minimum Query) problem.

Building the table. For each position and each such that i + 2^j - 1 \le n, the cell mas[i][j] stores the index of the minimum element on the segment [i; i + 2^j - 1] of length 2^j. For j = 0 the segment consists of one element, so mas[i][0] = i.

For j > 0, split the segment of length 2^j into two halves of length 2^{j-1}: [i; i + 2^{j-1} - 1] and [i + 2^{j-1}; i + 2^j - 1]. The indices of the minimums on the halves have already been computed — they are mas[i][j - 1] and mas[i + 2^{j-1}][j - 1]:

The minimum of the whole segment equals the smaller of the two minimums of the halves. Therefore, mas[i][j] equals whichever of the indices mas[i][j - 1] and mas[i + 2^{j-1}][j - 1] points to the smaller array element. The table is filled in increasing order of and contains O(n \log n) cells.

Answering a query. Suppose we need to find the minimum on the segment . Let k = \lfloor \log_2(v - u + 1) \rfloor be the exponent of the largest power of two not exceeding the length of the segment. The segment of length 2^k starting at and the segment of length 2^k ending at together completely cover , since 2 \cdot 2^k > v - u + 1:

These segments may overlap, but this does not matter for the minimum: an element counted twice does not change the result (unlike, for example, the sum). Therefore, the minimum on equals the smaller of the elements a[mas[u][k]] and a[mas[v - 2^k + 1][k]], and a query is processed in .

The bounds and of the generated queries may come in any order, so before executing a query we swap them if necessary.

Complexity. Building the table takes O(n \log n), and each query takes . The overall complexity is O(n \log n + m). With m \le 10^7, it is precisely the query time that makes it possible to fit into the time limit.

Example

Consider the example: n = 10, m = 8, a_1 = 12345. The generated array coincides with the array given in the problem statement. The table for it is shown below. The cells contain the indices of the minimum elements; a dash means that the segment goes beyond the array.

\boldsymbol{j \setminus i}

—

—

—

—

—

—

—

—

—

—

—

For example, mas[3][2] = 5: on the segment , the minimum element is a_5 = 1565158. The value mas[2][3] = 2 is obtained from mas[2][2] = 2 and mas[6][2] = 8, since a_2 = 305498 < a_8 = 570265.

The first query: u_1 = 3, v_1 = 9. The length of the segment is , so k = 2. The segment is covered by the segments and , for which mas[3][2] = 5 and mas[6][2] = 8. Since a_8 = 570265 < a_5 = 1565158, the answer is ans_1 = 570265.

The parameters of the second query:

u_2 = ((17 \cdot 3 + 751 + 570265 + 2) \bmod 10) + 1 = 10, \\ v_2 = ((13 \cdot 9 + 593 + 570265 + 5) \bmod 10) + 1 = 1.

Since u_2 > v_2, the bounds are swapped, and the minimum on the segment is computed.

All queries are listed in the table:

\boldsymbol{u_i}

\boldsymbol{v_i}

segment

\boldsymbol{ans_i}

The last query has the parameters u_8 = 5, v_8 = 3, and its answer is ans_8 = 1565158. These are exactly the values that are printed. Note that the original values u_8 and v_8 are printed, not the ordered bounds of the segment.

Algorithm implementation

The elements of the array (n \le 10^5) are stored starting from index . The sparse table is stored in the array : mas[i][j] is the index of the minimum element on the segment [i; i + 2^j - 1]. Since 2^{16} \le 10^5 < 2^{17}, the values of do not exceed , so LOGMAX = 17 is enough for the second dimension.

#define MAX 100001
#define LOGMAX 17
int a[MAX];
int mas[MAX][LOGMAX];

The function check swaps the values of and if u > v, so that u \le v. The parameters are passed by reference.

void check(int &u, int &v)
{
  int temp;
  if (u > v) temp = u, u = v, v = temp;
}

The function RMQ_nlogn builds the sparse table.

void RMQ_nlogn(void)
{
  int i, j;

First, mas[i][0] = i for all .

  for (i = 1; i <= n; i++) mas[i][0] = i;

Then, for each while 2^j \le n, and for each such that the segment [i; i + 2^j - 1] does not go beyond the array, choose from mas[i][j - 1] and mas[i + 2^{j-1}][j - 1] the index of the smaller element.

  for (j = 1; (1 << j) <= n; j++)
    for (i = 1; i + (1 << j) - 1 <= n; i++)
      if (a[mas[i][j - 1]] < a[mas[i + (1 << (j - 1))][j - 1]])
         mas[i][j] = mas[i][j - 1];
      else mas[i][j] = mas[i + (1 << (j - 1))][j - 1];
}

The function q returns the index of the minimum element on the segment . Compute k = \lfloor \log_2(j - i + 1) \rfloor and compare the minimums of two segments of length 2^k: [i; i + 2^k - 1] and [j - 2^k + 1; j].

int q(int i, int j)
{
  int k = (int)(log(1.0*j - i + 1) / log(2.0));
  int res = mas[i][k];
  if (a[mas[j - (1<<k) + 1][k]] < a[res])
      res = mas[j - (1<<k) + 1][k];
  return res;
}

The main part of the program. Read , , the first array element a_1, and the parameters of the first query and .

scanf("%d %d",&n,&m);
scanf("%d %d %d",&a[1],&u,&v);

Generate the elements a_2, a_3, \ldots, a_n using the formula from the problem statement. The product 23 \cdot a_i does not exceed 23 \cdot 16714589 \approx 3.8 \cdot 10^8 and fits into the type.

for(i = 2; i <= n; i++)
  a[i] = (23 * a[i-1] + 21563) % 16714589;

Build the sparse table .

RMQ_nlogn();

Process the queries one by one.

for (i = 1; i <= m; i++)
{

Copy the bounds of the current query into and and, if necessary, order them with the function check. The values and themselves are not changed: the parameters of the next query are computed from them, and they are what is printed in the answer.

  u1 = u; v1 = v; check(u1,v1);

Find the minimum element on the segment [u1; v1].

  ans = a[q(u1,v1)];

If the query is not the last one, compute the parameters of the next query using the formulas from the problem statement.

  if (i < m)
  {
    u = ((17*u + 751 + ans + 2*i) % n) + 1;
    v = ((13*v + 593 + ans + 5*i) % n) + 1;
  }
}

Print the parameters of the last query and its answer.

printf("%d %d %d\n",u,v,ans);