Сумма на квартале

тема: Префиксные суммы · уровень: средний

Условие

В городе сетка кварталов размера H×W. В каждом квартале записано число — насколько там “шумно” сегодня (может быть отрицательным: значит, там стало тише).

Мэрия задаёт много вопросов вида: «Каков общий шум на прямоугольном районе?» Район всегда задаётся двумя углами и включает все кварталы внутри.

Важно: сумма по району может не помещаться в 32-битный тип (как на некоторых языках), ориентируйтесь на 64-битные значения. В Python это не проблема.

Формат ввода

Первая строка: два целых числа H и W (1 ≤ H ≤ 250, 1 ≤ W ≤ 250). Далее H строк по W целых чисел a[i][j] (−10^6 ≤ a[i][j] ≤ 10^6). Затем целое число Q (1 ≤ Q ≤ 4000). Далее Q строк: r1 c1 r2 c2 — координаты прямоугольника (1 ≤ r1 ≤ r2 ≤ H, 1 ≤ c1 ≤ c2 ≤ W). Прямоугольник включает все клетки с строками от r1 до r2 и столбцами от c1 до c2.

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

Выведите Q строк. В i-й строке — сумму чисел в указанном прямоугольнике.

Ограничения

H, W ≤ 250, Q ≤ 4000. Значения в клетках от −10^6 до 10^6.

Пример

Ввод:

3 4
1 2 3 4
-5 0 7 1
2 2 2 2
2
1 1 2 3
2 2 3 4

Вывод:

8
14

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

Приём: 2D префиксные суммы

Ключевое наблюдение: если уметь быстро находить сумму в прямоугольнике от (1,1) до (r,c), то любой запросный прямоугольник можно собрать из четырёх таких сумм (принцип «включил-исключил»).

Приём: 2D префиксные суммы. Строим таблицу ps, где ps[r][c] — сумма всех клеток в прямоугольнике с углами (1,1) и (r,c). Тогда сумма любого района (r1..r2, c1..c2) считается за константное время.

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

(можно ускорить, накапливая сумму по строке).

Сложность: построение O(H*W), каждый запрос O(1), всего O(H*W + Q) по времени и O(H*W) по памяти.

Частая ошибка: перепутать знаки в формуле запроса или забыть сделать ps с «нулевой рамкой», из-за чего ломаются случаи с r1=1 или c1=1.

Разберись руками

Есть таблица 3×4 с “шумом” по кварталам. Приходят запросы: найти сумму чисел внутри прямоугольника, заданного двумя углами. На примере попробуем руками понять, как отвечать на запросы без пересчёта всех клеток каждый раз.

Идея: Сначала заранее посчитать для каждого угла сумму всего прямоугольника от (1,1) до этого угла. Тогда сумму любого прямоугольника можно получить из нескольких таких “угловых” сумм: берём большой прямоугольник, вычитаем лишнее сверху и слева, а то, что вычлось дважды, добавляем обратно.

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

Куда дальше