Конвейер и отчёт по первым секциям

тема: Fenwick/Segment tree · уровень: продвинутый

Условие

На заводе стоит длинный конвейер из n секций, пронумерованных от 1 до n. В каждой секции лежит некоторое число деталей (оно может быть отрицательным — например, если по учёту есть долг).

Диспетчер делает два типа действий:

Твоя задача — быстро отвечать на такие запросы.

Формат ввода

В первой строке задано целое число t — количество наборов данных. Далее идут t наборов данных.

В первой строке каждого набора заданы два целых числа n и q — количество секций и количество действий. Во второй строке задано n целых чисел a1, a2, ..., an — начальные значения по секциям. Далее идут q строк, каждая описывает одно действие одного из видов:

Формат вывода

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

Ограничения

Пример

Ввод:

3
1 1
0
SUM 1
1 3
-1000000
ADD 1 1000000
SUM 1
SUM 1
5 4
1 2 3 4 5
SUM 5
ADD 5 -10
SUM 5
SUM 1

Вывод:

0
0
0
15
5
1

Как решать — идея подхода

Приём: Дерево Фенвика

Если после каждого ADD пересчитывать все обычные префиксные суммы, придётся менять значения от позиции i до n. Это до n действий на один запрос, а при 65000 операциях может быть слишком долго.

Здесь подходит дерево Фенвика (Binary Indexed Tree). Это массив bit, где ячейка с номером i хранит сумму некоторого блока, оканчивающегося в i. Размер блока определяется последним установленным битом числа i:

lowbit(i) = i & -i

Например, bit[6] отвечает за блок из 2 позиций, а bit[8] — за блок из 8 позиций. Благодаря этому любой префикс можно разбить всего на несколько таких блоков.

План решения:

Обе операции делают не более O(log n) шагов, поэтому общее время — O((n + q) log n), память — O(n).

Частая ошибка — использовать индексацию с нуля. Формула i & -i и переходы дерева рассчитаны на позиции от 1; индекс 0 в цикле обновления приведёт к бесконечному циклу.

Решить задачу с автопроверкой на Python →

Куда дальше