Префиксные суммы - Алгоритмика
Префиксные суммы

Префиксные суммы

автор Егор Горбачев
редактор Сергей Слотин
опубликовано 2021
Определение. Префиксными суммами массива [a0,a1,a2,,an1][a_0, a_1, a_2, \ldots, a_{n - 1}] называется массив [s0,s1,s2,,sn][s_0, s_1, s_2, \ldots, s_n], определенный следующим образом: s0=0s1=a0s2=a0+a1s3=a0+a1+a2sn=a0+a1++an1 \begin{aligned} & s_0 = 0\\ & s_1 = a_0\\ & s_2 = a_0 + a_1\\ & s_3 = a_0 + a_1 + a_2\\ & \ldots\\ & s_n = a_0 + a_1 + \ldots + a_{n - 1} \end{aligned}

Обратите внимание, что в такой индексации

  • sks_k равен сумме первых kk элементов массива aa не включая aka_k,
  • длина ss на единицу больше длины aa,
  • s0s_0 всегда равен нулю.

Иногда префиксные суммы определяют включая правый конец и без нулевого элемента, то есть как sk=a0+a1++aks_k = a_0 + a_1 + \ldots + a_k, но по той же причине, почему отрезки почти всегда менее удобны, чем полуинтервалы, мы всегда будем работать с «полуинтрвальными» префиксными суммами из определения.

Формулу для sks_k можно записать рекуррентно как sk+1=sk+aks_{k+1} = s_k + a_k, что сразу дает метод подсчета префиксных сумм за линейное время:

int s[n + 1];

s[0] = 0;

for (int i = 0; i < n; i++)
    s[i + 1] = s[i] + a[i];

Задача. Дан массив целых чисел, и приходят запросы вида «найти сумму на полуинтервале с позиции ll до позиции rr». Нужно отвечать на запросы за O(1)O(1).

Предподсчитаем перед ответами на запросы массив префиксных сумм для исходного массива. Тогда если бы во всех запросах ll было равно нулю, то ответом на запрос была бы просто префиксная сумма srs_r. Но как действовать, если l0l \neq 0?

В префиксной сумме srs_r содержатся все нужные нам элементы, однако есть еще лишние, а именно a0,a1,,al1a_0, a_1, \ldots, a_{l - 1}. Заметим, что такая сумма в свою очередь равна уже посчитанной префиксной сумме sls_l. Таким образом, выполнено тождество:

al+al+1++ar1=srsl a_{l} + a_{l + 1} + \ldots + a_{r - 1} = s_{r} - s_{l}

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

#Другие операции

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

Что нам на самом деле здесь нужно было от сложения? Только то, что у сложения есть обратная операция — вычитание — с помощью которой можно по двум префиксам восстановить значение на отрезке, «отменив» sls_l. Сумма обратима, но например минимум или максимум необратимы — по значениям минимумов на префиксах в общем случае невозможно получить значение минимума на отрезке (в чем несложно убедиться, рассмотрев случай, когда первый элемент массива минимальный, и все префиксные суммы будут ему равны, все зависимости от остальных значений).

Помимо сложения, есть и другие операции, которые являются обратимыми:

  • побитовое исключающее «или», также известное как xor и обозначаемое \oplus,
  • сложение по модулю,
  • умножение и умножение по модулю (обратное — деление).

Помимо обычной суммой, самая популярная из них — xor, которая считается ещё проще:

alal+1ar1=srsl a_l \oplus a_{l + 1} \oplus \ldots \oplus a_{r - 1} = s_r \oplus s_l

#Задачи на префиксные суммы

Задача. Дан массив целых чисел. Определите, есть ли в нём подотрезок заданной суммы за линейное время.

Задача. Даны два массива одинаковой длины. Найдите такой подотрезок, что сумма элементов первого массива совпадала с суммой элементов второго массива (на индексах этого отрезка).

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