Бесплатная бета: 60 дней полного доступа, без карты.мест осталось: 84Зарегистрироваться бесплатно

Мы используем необходимые куки для работы сайта (вход и язык). Если ты согласишься, мы также загрузим Google Analytics, чтобы видеть, какие страницы читают, и Google reCAPTCHA для защиты форм обратной связи и сообщений об ошибке от спама. Политика конфиденциальности

← Блог
Как готовиться

Префиксные суммы: сумма на отрезке за O(1) и подсчёт подмассивов

· 2026-10-15

Короткий ответ. Префиксные суммы это массив, где pref[i] хранит сумму первых i элементов. После одного прохода сумма любого отрезка считается за O(1): pref[r + 1] - pref[l]. Приём нужен, когда запросов на сумму много, а массив не меняется. С хеш-таблицей он ещё и считает подмассивы с заданной суммой. Ниже устройство, код на Python и разбор задачи Subarray Sum Equals K.

Что такое префиксные суммы?

Берём массив a. Строим массив pref длины n + 1. pref[0] равен 0, pref[i] равен сумме a[0] .. a[i - 1].

Сумма отрезка от l до r включительно это pref[r + 1] - pref[l]. Всё, что до l, вычитается. Остаётся ровно отрезок.

Построение стоит O(n). Каждый запрос после этого стоит O(1). Без префиксов каждый запрос стоил бы O(n). При тысяче запросов разница уже в тысячу раз.

Суммы на отрезке через массив префиксов
rangeSum(buildPrefix([3, 1, 4, 1, 5, 9]), 1, 4)
arr
3
1
4
1
5
9
prefix (prefix[i] = сумма первых i значений)
0

Строим prefix на одну ячейку длиннее arr. prefix[0] это сумма ничего, поэтому там 0.

Гонка скорости
40 сумм на отрезках по одним и тем же 40 числам

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

Одна операция: одно чтение значения. За каждый кадр все дорожки делают одинаковое число операций, поэтому медленная отстаёт.

Цикл по каждому отрезку O(n·q)
операций: 0
Префиксные суммы O(n + q)
операций: 0

 

А если чисел больше

Операции для n чисел и примерное время при 100 миллионах простых операций в секунду. Это оценка по тому же счёту, а не замер.

n = 1 000
Цикл по каждому отрезку334,3 тыс. · 3,3 мс
Префиксные суммы3 тыс. · меньше 1 мс
n = 100 000
Цикл по каждому отрезку3,3 млрд · 33 с
Префиксные суммы300 тыс. · 3 мс
n = 10 000 000
Цикл по каждому отрезку33,3 трлн · 3,9 дн.
Префиксные суммы30 млн · 300 мс

Страница паттерна: когда его применять и шаблон на шести языках

По каким словам в задаче узнать префиксные суммы?

  • «Много запросов вида сумма на отрезке».
  • «Сколько подмассивов с суммой k».
  • «Индекс, где сумма слева равна сумме справа».
  • «Самый длинный подмассив с суммой 0» или «с равным числом нулей и единиц».

Общий признак: сумма отрезка нужна много раз, а массив не меняется. Если массив меняется между запросами, нужно дерево Фенвика.

Как это выглядит на примере?

Массив [2, 4, 1, 3]. pref = [0, 2, 6, 7, 10]. Сумма от индекса 1 до 2, то есть 4 + 1: pref[3] - pref[1] = 7 - 2 = 5.

Сумма всего массива: pref[4] - pref[0] = 10. Сумма пустого отрезка от 2 до 1: pref[2] - pref[2] = 0. Лишняя нулевая ячейка в начале убирает особый случай для l = 0.

Как написать префиксные суммы на Python?

from itertools import accumulate

def build(a):
    pref = [0]
    for x in a:
        pref.append(pref[-1] + x)
    return pref                      # или list(accumulate(a, initial=0))

def range_sum(pref, l, r):
    return pref[r + 1] - pref[l]     # сумма a[l..r]

def subarray_sum_equals_k(nums, k):
    seen = {0: 1}                    # префикс 0 встречен один раз
    total = count = 0
    for x in nums:
        total += x
        count += seen.get(total - k, 0)
        seen[total] = seen.get(total, 0) + 1
    return count

Третья функция не хранит массив pref. Ей хватает текущей суммы и словаря, сколько раз каждая сумма встречалась.

Как префиксные суммы решают Subarray Sum Equals K?

Нужно число подмассивов с суммой k. Подмассив от l до r имеет сумму pref[r + 1] - pref[l]. Она равна k, когда pref[l] равен pref[r + 1] - k.

Идём слева направо. На каждом шаге смотрим, сколько раз раньше встречался префикс total - k. Столько подмассивов заканчивается здесь. Потом записываем текущий префикс в словарь.

Запись {0: 1} в начале учитывает подмассивы с самого начала. Без неё ответ будет меньше на число таких подмассивов.

Чем префиксные суммы отличаются от соседних приёмов?

ПриёмМассив меняетсяЗапросОтрицательные числа
Префиксные суммыНетO(1)Да
Скользящее окноНетO(n) на весь проходНет, условие не монотонно
Дерево ФенвикаДаO(log n)Да
Массив разностейМного прибавлений на отрезкахO(1) на обновлениеДа

Как перенести приём на матрицу?

Двумерный префикс pref[i][j] хранит сумму прямоугольника от угла (0, 0) до (i - 1, j - 1). Строится за O(n · m).

Сумма прямоугольника считается по формуле включений и исключений. Вычитаем два прямоугольника выше и левее, потом возвращаем пересечение, которое вычли дважды. Четыре чтения, O(1).

Как решить задачу про подмассив с равным числом нулей и единиц?

Замените каждый ноль на минус один. Теперь нужен подмассив с суммой ноль. Идём с текущей суммой и словарём, где сумма встретилась впервые. Повторилась сумма, значит между двумя встречами лежит подмассив с нулевой суммой. Длина это разница индексов.

На каких ошибках ломается решение?

  • Массив pref длины n вместо n + 1. Сумма с нулевого индекса требует особого случая.
  • Записывают текущий префикс в словарь до проверки. Подмассив нулевой длины начинает считаться.
  • Забывают {0: 1} и теряют подмассивы с начала.
  • Берут префиксы там, где массив меняется. Каждое изменение стоит O(n) на пересчёт.

Как объяснить приём на собеседовании?

Скажите: много запросов суммы на отрезке, массив не меняется. Назовите pref и формулу разности. Назовите O(n) на построение и O(1) на запрос.

Если задача про число подмассивов, добавьте словарь. Скажите: «ищу префикс total - k среди прошлых». Это ключевая фраза.

Отработать узнавание приёма можно на странице префиксные суммы. Соседний приём разобран на странице массив разностей.

Вопросы и ответы

Можно ли строить префиксы для произведения или XOR?

Да, для любой операции с обратной. XOR обратен сам себе. Произведение ломается на нулях, там нужен особый учёт.

Как найти индекс, где сумма слева равна сумме справа?

Посчитайте общую сумму total. Идите по массиву, держите сумму слева. Сумма справа это total минус левая минус текущий элемент. Первое совпадение и есть ответ. Один проход, O(1) памяти.

Что делать, если массив меняется?

Пересчитывать префиксы после каждого изменения слишком дорого. Берите дерево Фенвика: O(log n) на изменение и на запрос.

Где приём встречается в реальных системах?

В аналитике: сумма продаж за любой период из накопленных итогов. В графике: интегральные изображения для быстрого размытия. В базах данных: агрегаты по диапазонам.

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