Двумерное дерево отрезков с массовыми операциями

Правка ru13, от Renedyn, 2021-09-14 13:10:49

Решим следующую задачу:

Дана таблица $$$n \cdot m$$$, поступают 2 вида запросов в онлайне:

1) Прибавить $$$x$$$ на прямоугольнике $$$[x1, x2], [y1, y2]$$$, помимо ассоциативности операция должна быть коммутативна, например присваивание не подойдёт.

2) Посчитать сумму на прямоугольнике $$$[x1, x2], [y1, y2]$$$.

Почему нельзя использовать пуши? Для массовых операций с пушами должно быть выполнено свойство, что отложенная операция может складываться, но в случае 2Д ДО мы должны складывать не числа, а отрезки, и их объединять уже не получиться. Но помимо пушей есть другой тип массовых операций.

Сперва научимся делать прибавление на отрезке и сумму на отрезке без пушей в одномерном ДО.

В каждой вершине будем хранить $$$sum[v]$$$ и $$$add[v]$$$ — сумму на отрезке и сколько мы прибавим каждому элементу на отрезке. Рассмотрим запрос прибавления: для всех вершин, посещенных нашей функцией (на картинке желтые и зелёные), мы корректно пересчитаем значение в вершине, для суммы это $$$sum[v] += (min(r, rx)-max(l, lx)) \cdot x$$$, но сумма в вершинах ниже останется старой (красные). Чтобы это учесть, в тех вершинах, в которых остановилась функция (зелёные) сделаем $$$add[v] += x$$$.

Номер корня равен 0, реализация на полуинтервалах

void upd(int l, int r, int lx, int rx, int v, int x) {
    if (l >= rx || r <= lx) return;
    sum[v] += (min(r, rx)-max(l, lx)) * x;

    if (l >= lx && r <= rx) {
        add[v] += x;
        return;
    }
    int m = (l + r) / 2;
    upd(l, m, lx, rx, v*2+1, x);
    upd(m, r, lx, rx, v*2+2, x);
}

Теперь запрос суммы. Чтобы узнать актуальную сумму в вершине, нужно взять сумму $$$add[p]$$$ по всем предкам, умноженную на длину отрезка, и $$$sum[v]$$$. Чтобы лучше понять, почему это так, попробуйте посчитать сумму для красной вершины на картинке. Функция запроса будет такой же, как и в обычном ДО, только нужно поддерживать сумму на пути от корня до вершины.

В реализации можно даже не поддерживать сумму на пути, а просто когда мы спускаемся добавлять к ответу $$$add[v] \cdot (min(r, rx)-max(l, lx))$$$, это равносильно тому, что описано выше. Позже именно такой способ будет использоваться для больших размерностей.

int ask(int l, int r, int lx, int rx, int v) {
    if (l >= rx || r <= lx) return 0;
    if (l >= lx && r <= rx) {
        return sum[v];
    }
    int m = (l + r) / 2;
    return ask(l, m, lx, rx, v*2+1) + ask(m, r, lx, rx, v*2+2) + (min(r, rx)-max(l, lx)) * add[v];
}

Теперь этот же способ можно использовать для двумерного ДО.

Сделаем ДО по первой координате. В вершине, отвечающей за отрезок $$$[x1,x2]$$$ будет массив, отвечающий за прямоугольник $$$[x1, x2], [0, m-1]$$$, где i-тый элемент равен $$$\displaystyle\sum_{i = x1...x2} mas[j][i]$$$, в частности, в листах ДО будут просто строки таблицы, а в корне будет массив сумм столбцов.

Sorry for my bad Paint

Теги структуры данных, дерево отрезков

История

 
 
 
 
Правки
 
 
  Rev. Язык Кто Когда Δ Комментарий
ru26 Русский Renedyn 2021-09-28 10:55:50 165
ru25 Русский Renedyn 2021-09-19 13:43:08 52 Мелкая правка: 'aGhizX\n\n\n' -> 'aGhizX\n\nЗадачи: https://codeforces.com/contest/341/problem/D\n'
ru24 Русский Renedyn 2021-09-18 22:41:16 1 Мелкая правка: 'не получиться. Но пом' -> 'не получится. Но пом'
ru23 Русский Renedyn 2021-09-16 23:35:10 0 (опубликовано)
ru22 Русский Renedyn 2021-09-16 23:34:42 6 Мелкая правка: 'ет $O(8^k * n)$ и оди' -> 'ет $O(8^k \cdot n)$ и оди' (сохранено в черновиках)
ru21 Русский Renedyn 2021-09-16 21:14:15 1 (опубликовано)
ru20 Русский Renedyn 2021-09-16 21:13:45 38
ru19 Русский Renedyn 2021-09-16 21:10:55 150 Мелкая правка: 'ять будет линейная. \n\nНаск' -> 'ять будет $O(n \cdot log n)$. \n\nНаск'
ru18 Русский Renedyn 2021-09-16 17:26:20 258 Мелкая правка: '87-ru.pdf]А3 недавно п' -> '87-ru.pdf](А3) недавно п'
ru17 Русский Renedyn 2021-09-16 14:31:49 535
ru16 Русский Renedyn 2021-09-16 10:54:11 121 Мелкая правка: '(8^k * n)$. \n\n' -> '(8^k * n)$ и один запрос $O(log^k n)$. \n\n'
ru15 Русский Renedyn 2021-09-15 11:55:10 201
ru14 Русский Renedyn 2021-09-15 11:22:03 2592 Мелкая правка: ' отрезке $[l2, r2]$ $x \cdot (m' -> ' отрезке $sum[v][l2...r2] += x \cdot (m'
ru13 Русский Renedyn 2021-09-14 13:10:49 4 Мелкая правка: 'Paint_\n\n' -> 'Paint_\n\n\n\n'
ru12 Русский Renedyn 2021-09-14 12:57:01 292 Мелкая правка: 'mas[j][i]$\n\n' -> 'mas[j][i]$ \cos (2\theta) = \cos^2 \theta - \sin^2 \theta\n\n'
ru11 Русский Renedyn 2021-09-14 11:18:20 42
ru10 Русский Renedyn 2021-09-14 10:54:15 781 Мелкая правка: 'это учесть в тех вер' -> 'это учесть, в тех вер'
ru9 Русский Renedyn 2021-09-14 09:23:05 95
ru8 Русский Renedyn 2021-09-14 08:59:04 4 Мелкая правка: ' значение d вершинt, для сумм' -> ' значение в вершине, для сумм'
ru7 Русский Renedyn 2021-09-14 08:50:16 1137 Мелкая правка: ' (красные), чтобы это исправить в тех в' -> ' (красные). Чтобы это учесть в тех в'
ru6 Русский Renedyn 2021-09-13 21:48:03 479 Мелкая правка: '075.png)\n\n\n\nСд' -> '075.png)\nТеперь запрос суммы. \n\n\n\nСд'
ru5 Русский Renedyn 2021-09-13 20:05:04 328 Мелкая правка: 'м $add[v] += x$. \n![ ' -> 'м $add[v] <+=> x$. \n![ '
ru4 Русский Renedyn 2021-09-13 19:10:20 68
ru3 Русский Renedyn 2021-09-13 19:08:40 337 Мелкая правка: 'а таблица n * m, поступаю' -> 'а таблица $n \cdo m$, поступаю'
ru2 Русский Renedyn 2021-09-13 17:16:04 530 Мелкая правка: 'Прибавить X' -> 'Прибавить _X_'
ru1 Русский Renedyn 2021-09-13 16:17:16 149 Первая редакция (сохранено в черновиках)