Как эффективно отвечать на запросы диапазона в массиве целых чисел?

Как эффективно и ранжировать запросы в массиве целых чисел?

Запросы бывают только одного типа, т. е. для заданного диапазона [a,b] нужно найти sum of elements, равные less than x (здесь x — часть каждого запроса, скажем, в форме a b x).

Первоначально я пытался буквально перейти от a к b и проверить, меньше ли текущий элемент, чем x, и сложить. Но этот способ очень неэффективен, так как сложность составляет O(n).

Теперь я пытаюсь использовать деревья сегментов и сортировать числа при слиянии. Но теперь моя проблема заключается в том, что если я сортирую, то я теряю относительный порядок целых чисел. Поэтому, когда приходит запрос, я не могу использовать отсортированный массив для получения значений от a до b.


person user3243499    schedule 14.07.2017    source источник
comment
Можете ли вы сделать пакетную обработку? то есть сначала прочитать все запросы и вывести ответы каждого запроса   -  person Petar Petrovic    schedule 14.07.2017
comment
Да, доступно n запросов   -  person user3243499    schedule 14.07.2017


Ответы (1)


Вот два подхода к решению этой проблемы с деревьями сегментов:

Подход 1

Вы можете использовать дерево сегментов отсортированных массивов.

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

Получив запрос, вы сначала обрабатываете поддиапазон O(log(n)), который охватывает ваш диапазон [a,b]. Для каждого из них вы используете двоичный поиск O (log (n)). В целом это сложность O(qlog^2n) для ответа на q запросов (плюс время предварительной обработки).

Подход 2

Вы можете использовать динамическое дерево сегментов.

Дерево сегментов позволяет отвечать на запросы вида «Вычислить сумму элементов от a до b» за время O(logn), а также изменять одну запись за время O(logn).

Поэтому, если вы начинаете с пустого дерева сегментов, вы можете повторно вставить записи в порядке возрастания. Предположим, мы добавили все записи от 1 до 5, поэтому наш массив может выглядеть так:

[0,0,0,3,0,0,0,2,0,0,0,0,0,0,1,0,0,0,4,4,0,0,5,1]

(Нули представляют записи, которые больше 5, поэтому они еще не были добавлены.) На этом этапе вы можете отвечать на любые запросы, порог которых равен 5.

В целом это будет стоить O(nlog(n)) для добавления всех записей в дерево сегментов, O(qlog(q)) для сортировки запросов и O(qlog(n)) для использования дерева сегментов для ответов на запросы. .

person Peter de Rivaz    schedule 14.07.2017
comment
Не могли бы вы подробнее рассказать о подходе 1? Какие поддиапазоны вы имеете в виду? Все возможные поддиапазоны O (n ^ 2)? - person Nico Schertler; 14.07.2017
comment
@NicoSchertler Предположим, у нас есть массив длиной 2 ^ 5, дерево сегментов будет представлять 1 диапазон длины 2 ^ 5, 2 длины 2 ^ 4, 4 длины 2 ^ 3, 8 длины 2 ^ 2, 16 длины 2 и 32 длины 1. Всего представлено 1+2+4+8+16+32=63=2*n-1 поддиапазонов. - person Peter de Rivaz; 14.07.2017