Редакция для Диапазонные прибавления и сумма на отрезке
Submitting an official solution before solving the problem yourself is a bannable offence.
Автор:
1. Идея
Нужно выполнить k операций вида: прибавить число x ко всем элементам на отрезке [l, r], а потом ответить на q запросов суммы на отрезке [L, R].
Если делать каждую прибавку поэлементно, получится слишком медленно: один запрос обновления может затронуть до n грядок, а таких команд до 2 * 10^5.
Главная идея состоит из двух шагов:
- Сначала быстро применить все диапазонные прибавления с помощью разностного массива.
- Затем по полученному массиву количеств воды построить префиксные суммы, чтобы быстро отвечать на запросы суммы на отрезке.
2. Наблюдения
Наблюдение 1. Как быстро прибавить число ко всему отрезку
Пусть есть массив a[1..n]. Чтобы прибавить x ко всем элементам на отрезке [l, r], можно не менять все элементы сразу, а использовать разностный массив d.
Для операции [l, r] += x достаточно сделать:
d[l] += xd[r + 1] -= x
Тогда после восстановления массива префиксными суммами получится, что:
- начиная с позиции
lзначение увеличится наx, - после позиции
rэто увеличение отменится.
То есть все k команд можно записать в d за O(k).
Наблюдение 2. Как восстановить итоговый массив
Если d — разностный массив, то итоговое количество воды на грядке i равно:
a[i] = a[i - 1] + d[i]
Так мы за один проход получаем весь массив a[1..n].
Наблюдение 3. Как быстро отвечать на суммы на отрезке
После того как известен массив a, нужно много раз находить сумму на отрезке [L, R].
Для этого строим массив префиксных сумм pref:
pref[i] = pref[i - 1] + a[i]
Тогда ответ на запрос:
- сумма на
[L, R]равнаpref[R] - pref[L - 1]
Каждый запрос обрабатывается за O(1).
3. Алгоритм
- Считать
n,k,q. - Создать разностный массив
dразмераn + 3, заполненный нулями. - Для каждой из
kкоманд:- считать
l,r,x, - сделать
d[l] += x, - сделать
d[r + 1] -= x.
- считать
- Создать массивы
aиprefразмераn + 1. - Для
iот1доn:- восстановить значение на грядке:
a[i] = a[i - 1] + d[i], - посчитать префиксную сумму:
pref[i] = pref[i - 1] + a[i].
- восстановить значение на грядке:
- Для каждого из
qзапросов:- считать
L,R, - вывести
pref[R] - pref[L - 1].
- считать
4. Почему это работает
Докажем корректность по шагам.
Шаг 1. Корректность разностного массива
Рассмотрим одну команду: прибавить x ко всем грядкам от l до r.
После операций:
d[l] += xd[r + 1] -= x
при переходе к префиксным суммам:
- для позиций меньше
lэта прибавка не влияет, - начиная с
lв накопленной сумме появляется+x, - после
rприбавка компенсируется значением-xвd[r + 1].
Значит, ровно на отрезке [l, r] все элементы увеличиваются на x.
Если таких команд несколько, их влияние просто складывается, потому что все изменения линейны: вклады всех операций суммируются.
Следовательно, после восстановления массива a каждая грядка содержит ровно итоговое количество воды после всех команд.
Шаг 2. Корректность префиксных сумм
По определению:
pref[i]— это сумма элементовa[1] + a[2] + ... + a[i].
Тогда сумма на отрезке [L, R] равна:
- сумма от
1доR, - минус сумма от
1доL - 1.
То есть ответ равен pref[R] - pref[L - 1].
Таким образом, алгоритм правильно отвечает на каждый запрос.
5. Сложность
- Обработка всех
kкоманд:O(k) - Восстановление массива и построение префиксных сумм:
O(n) - Ответы на все
qзапросов:O(q)
Итоговая сложность:
O(n + k + q)
Память:
O(n)
Это хорошо укладывается в ограничения.
6. Код на C++17
#include <iostream>
#include <vector>
using namespace std;
int main() {
int n, k, q;
cin >> n >> k >> q;
vector<long long> d(n + 3, 0);
for (int i = 0; i < k; i++) {
int l, r;
long long x;
cin >> l >> r >> x;
d[l] += x;
d[r + 1] -= x;
}
vector<long long> a(n + 1, 0);
vector<long long> pref(n + 1, 0);
for (int i = 1; i <= n; i++) {
a[i] = a[i - 1] + d[i];
pref[i] = pref[i - 1] + a[i];
}
for (int i = 0; i < q; i++) {
int L, R;
cin >> L >> R;
cout << pref[R] - pref[L - 1] << '\n';
}
return 0;
}
7. Код на Python 3
n, k, q = map(int, input().split())
d = [0] * (n + 3)
for _ in range(k):
l, r, x = map(int, input().split())
d[l] += x
d[r + 1] -= x
a = [0] * (n + 1)
pref = [0] * (n + 1)
for i in range(1, n + 1):
a[i] = a[i - 1] + d[i]
pref[i] = pref[i - 1] + a[i]
for _ in range(q):
L, R = map(int, input().split())
print(pref[R] - pref[L - 1])
Комментарии