3866. K-ое число

 

Вы работаете на компанию МакроХард в отделе структуры данных. После неудачного решения предыдущей задаче о вставки ключей, Вас попросили написать новую структуру данных, позволяющую быстро находить k-ую порядковую статистику на указанном отрезке.

Задан массив a[1...n] различных целых чисел. Необходимо отвечать на вопросы Q(i, j, k) следующего вида: "Найти k-ое число на отрезке a[i ... j], если все числа на этом отрезке предварительно отсортировать".

Например, рассмотрим отрезок a = (1, 5, 2, 6, 3, 7, 4). Рассмотрим запрос Q(2, 5, 3). Отрезком a[2 ... 5] будет (5, 2, 6, 3). Отсортировав числа, получим (2, 3, 5, 6). Третьим элементом будет 5. Ответом на запрос будет 5.

 

Вход. Первая строка содержит размер массива n и количество запросов m (1 ≤ n ≤ 100000, 1 ≤ m ≤ 5000).

Вторая строка содержит n разных целых чисел, не превосходящих 109 по абсолютному значению – массив чисел, к которому будут ставиться запросы.

Следующие m строк содержат запросы, каждый из которых состоит из трех чисел: i, j и k (0 ≤ ijn, 0 ≤ kji + 1) и представляет собой запрос Q(i, j, k).

 

Выход. Для каждого запроса вывести k-ое число в отсортированном отрезке a[i ... j].

 

Пример входа

Пример выхода

7 3

1 5 2 6 3 7 4

2 5 3

4 4 1

1 7 3

5

6

3

 

 

РЕШЕНИЕ

структуры данных – дерево отрезков

 

Анализ алгоритма

Заведем в каждой вершине дерева отрезков дополнительный массив. Вершина, соответствующая фундаментальному отрезку [i; j], содержит в своем массиве отсортированный набор чисел ai, …, aj.

Пусть запрос Query(l, r, x) находит количество чисел из интервала [l, r], которые меньше x. Для этого разобьем этот интервал на множество фундаментальных отрезков, в каждом из которых совершим бинарный поиск, подсчитав число элементов, меньших x. Время запроса составит .

Ответ на запрос Q(i, j, k) будем искать бинарным поиском. Следует найти такое ap из интервала [ai, …, aj], что Query(l, r, ap) равно k – 1. То есть если ap является k –ым элементов на отрезке [i; j], то количество чисел на этом отрезке, меньших ap, равно k – 1. Принципиальным в этой задаче является то, что все входные числа разные. Таким образом запрос выполняется за .

 

Пример

Рассмотрим выполнение запросов:

 

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

В массив а считываем входные числа.

 

#define MAX 100010

int a[MAX];

 

Объявим дерево отрезков SegTree, в каждой вершине которого будет храниться еще один линейный массив.

 

vector<vector<int> > SegTree;

 

Построение дерева отрезков. Листы хранят массив из одного элемента.

 

void build(int Vertex, int LeftPos, int RightPos)

{

  if (LeftPos == RightPos)

    SegTree[Vertex].push_back(a[LeftPos]);

  else

  {

    int Middle = (LeftPos + RightPos) / 2;

    build (2*Vertex, LeftPos, Middle);

    build (2*Vertex+1, Middle+1, RightPos);

    SegTree[Vertex].resize(RightPos - LeftPos + 1);

 

В каждой вершине хранится остортированный список элементов, которые она покрывет.

 

    merge(SegTree[2*Vertex].begin(),SegTree[2*Vertex].end(),

          SegTree[2*Vertex+1].begin(),SegTree[2*Vertex+1].end(),

          SegTree[Vertex].begin());

  }

}

 

Возвращаем количество элементов на промежутке [Left; Right], строго меньших x.

 

int Query(int Vertex, int LeftPos, int RightPos,

          int Left, int Right, int x)

{

  if (Left > Right) return 0;

  if ((LeftPos == Left) && (RightPos == Right))

 

Дошли до вершины Vertex, которой соответствует фундаментальный отрезок [LeftPos; RightPos] = [Left; Right]. В ней хранится отсортированный массив чисел. При помощи бинарного поиска ищем в нем количество элементов, меньших x.

 

  return lower_bound(SegTree[Vertex].begin(),SegTree[Vertex].end(),x)

          – SegTree[Vertex].begin();

 

  int Middle = (LeftPos + RightPos) / 2;

  return Query(2*Vertex, LeftPos, Middle, Left, min(Middle,Right), x)

+ Query(2*Vertex+1, Middle+1, RightPos, max(Left,Middle+1), Right, x);

}

 

Основная часть программы. Читаем входные данные.

 

scanf("%d %d",&n,&m);

for(i = 1; i <= n; i++)

  scanf("%d",&a[i]);

 

Строим дерево отрезков.

 

SegTree.resize(4*n);

build(1,1,n);

 

Сортируем массив чисел.

 

sort(a+1,a+n+1);

 

Обрабатываем m запросов.

 

while(m--)

{

    scanf("%d %d %d",&i,&j,&k);

 

В отсортированном массиве а ищем бинарным поиском такое число a[mid], что на промежутке [i; j] имеется в точности k – 1 число, меньшее a[mid]. Бинарный поиск продолжаем, пока промежуток поиска [left; right] содержит более одного элемента.

 

  int left = 1, right = n;

  while (left < right)

  {

    int mid = (left + right) / 2;

    if (Query(1, 1, n, i, j, a[mid] + 1) < k)

      left = mid + 1;

    else

      right = mid;

  }

 

Выводим ответ.

 

  printf("%d\n", a[left]);

}