Frequent values - 2#4081

Frequent values - 2 (Editorial)

Задача отличается от задачи 3838 "Частые значения" увеличенными ограничениями на и . Последовательность задаётся один раз и не изменяется, поэтому для ответа на запросы воспользуемся разреженной таблицей (Sparse Table), которая находит максимум на отрезке за .

Блоки равных элементов. Последовательность упорядочена по неубыванию, поэтому равные элементы стоят подряд и образуют блоки. Количество вхождений значения на отрезке равно длине пересечения его блока с этим отрезком.

Массив . Построим массив , в котором b_i — номер элемента a_i внутри своего блока, то есть количество элементов, равных a_i, от начала блока до позиции включительно:

На рисунке показаны массив a = (3, 3, 5, 9, 9, 9, 9, 10, 10) и построенный по нему массив . Блоки равных элементов выделены чередующимися цветами, над каждым блоком указана его длина.

Внутри блока значения последовательно принимают значения 1, 2, 3, \ldots. Поэтому наибольшее значение в блоке достигается в его последнем элементе и равно длине блока. Эти элементы выделены оранжевым цветом.

Ответ на запрос. Рассмотрим запрос .

Все остальные блоки, пересекающие отрезок, начинаются внутри него — на позиции k + 1 или правее. Поэтому для любой позиции t \in [k + 1; j] значение b_t отсчитывается от начала блока, лежащего внутри отрезка, и не учитывает лишних элементов. Последний блок может обрываться на позиции , но тогда b_j равно количеству его элементов на отрезке. Следовательно, наибольшее количество вхождений среди этих блоков равно максимуму на [k + 1; j], и ответ на запрос равен

\max\bigl(k - i + 1, \mathit{RMQ}(k + 1, j)\bigr),

где \mathit{RMQ}(l, r) — максимум элементов b_l, \ldots, b_r.

Первый блок нельзя обработать с помощью массива : он может начинаться левее позиции , и тогда значения в нём учитывают элементы, не входящие в отрезок.

Позицию находим бинарным поиском: это позиция, предшествующая первому элементу, большему a_i. Поскольку a_j > a_i, такой элемент существует на отрезке [i + 1; j].

Разреженная таблица. Таблица строится по массиву и хранит не индексы, а сами максимальные значения: позиция максимума в этой задаче не нужна.

Сложность. Построение разреженной таблицы для каждого теста выполняется за O(n \log n). Запрос состоит из бинарного поиска за O(\log n) и запроса максимума за . Итоговая сложность теста составляет O(n \log n + q \log n), используемая память — O(n \log n).

Пример

Рассмотрим пример. Массив и построенный по нему массив имеют следующий вид:

1

2

3

4

5

6

7

8

9

10

\boldsymbol{a_i}

1

1

1

1

3

10

10

10

\boldsymbol{b_i}

1

2

1

2

3

4

1

1

2

3

Массив состоит из четырёх блоков: , (1, 1, 1, 1), и (10, 10, 10) длины , , и соответственно. Последний элемент каждого блока содержит в длину блока.

Запрос . Так как a_2 = -1 \ne a_3 = 1, находим k = 2. Первый блок даёт 2 - 2 + 1 = 1 вхождение, \mathit{RMQ}(3, 3) = b_3 = 1. Ответ равен \max(1, 1) = 1.

Запрос . Так как a_1 = -1 \ne a_{10} = 10, находим k = 2. Первый блок даёт вхождения, \mathit{RMQ}(3, 10) = \max(1, 2, 3, 4, 1, 1, 2, 3) = 4. Ответ равен \max(2, 4) = 4: значение встречается раза.

Запрос . Так как a_5 = 1 \ne a_{10} = 10, находим k = 6. Первый блок даёт 6 - 5 + 1 = 2 вхождения, \mathit{RMQ}(7, 10) = \max(1, 1, 2, 3) = 3. Ответ равен \max(2, 3) = 3.

Этот запрос показывает, почему первый блок обрабатывается отдельно. Значения b_5 = 3 и b_6 = 4 учитывают элементы a_3 и a_4, которые лежат вне отрезка. Если бы мы вычислили максимум на всём отрезке , то получили бы неверный ответ .

Реализация алгоритма

Объявим константы и массивы. Входная последовательность хранится в массиве , вспомогательный массив описан выше. Значение равно \lfloor \log_2 \mathit{len} \rfloor. Разреженная таблица \mathit{mas}[i][j] хранит максимум элементов на отрезке [i; i + 2^j - 1]. Поскольку 2^{18} \le 5 \cdot 10^5 < 2^{19}, значения не превышают , и для второго измерения достаточно \mathit{LOGMAX} = 19.

#define MAX 500010
#define LOGMAX 19
using namespace std;
int a[MAX], b[MAX], lg2[MAX];
int mas[MAX][LOGMAX];

Функция Build_RMQ_Array строит разреженную таблицу по массиву .

void Build_RMQ_Array(int* b)
{
  for (int i = 1; i <= n; i++) mas[i][0] = b[i];
  for (int j = 1; 1 << j <= n; j++)
    for (int i = 1; i + (1 << j) - 1 <= n; i++)
      mas[i][j] = max(mas[i][j - 1], mas[i + (1 << (j - 1))][j - 1]);
}

Функция RMQ возвращает максимум элементов b_i, \ldots, b_j за .

int RMQ(int i, int j)
{
  int k = lg2[j - i + 1];
  return max(mas[i][k], mas[j - (1 << k) + 1][k]);
}

Основная часть программы. Значения вычисляются один раз для всех длин до и используются во всех тестах.

for (int i = 2; i < MAX; i++) lg2[i] = lg2[i / 2] + 1;

Обрабатываем тесты, пока n \ne 0. Читаем последовательность и одновременно строим массив : если элемент равен предыдущему, его номер в блоке на единицу больше, иначе с него начинается новый блок.

while (scanf("%d %d", &n, &q), n)
{
  scanf("%d", &a[1]); b[1] = 1;
  for (i = 2; i <= n; i++)
  {
    scanf("%d", &a[i]);
    if (a[i] == a[i - 1]) b[i] = b[i - 1] + 1; else b[i] = 1;
  }

Строим разреженную таблицу по массиву .

  Build_RMQ_Array(b);

Последовательно обрабатываем запросов. Если a_{\mathit{Left}} = a_{\mathit{Right}}, то все элементы отрезка равны, и ответ равен \mathit{Right} - \mathit{Left} + 1.

  for (i = 0; i < q; i++)
  {
    scanf("%d %d", &Left, &Right);
    if (a[Left] == a[Right]) printf("%d\n", Right - Left + 1);

Иначе находим — последнюю позицию блока, содержащего a_{\mathit{Left}}. Функция upper_bound на отрезке [\mathit{Left}; \mathit{Right}) возвращает указатель на первый элемент, больший a_{\mathit{Left}}. Такой элемент существует, так как a_{\mathit{Right}} > a_{\mathit{Left}}. Предыдущая позиция и есть . Ответ равен \max\bigl(\mathit{LeftEnd} - \mathit{Left} + 1, \mathit{RMQ}(\mathit{LeftEnd} + 1, \mathit{Right})\bigr).

    else
    {
      int LeftEnd =
          (int)(upper_bound(a + Left, a + Right, a[Left]) - a) - 1;
      res = max(LeftEnd - Left + 1, RMQ(LeftEnd + 1, Right));
      printf("%d\n", res);
    }
  }
}