Несогласованные поддеревья. Реализация массового обновления — различия между версиями
Gr1n (обсуждение | вклад) (→Несогласованные поддеревья) |
Gr1n (обсуждение | вклад) (→Массовое обновление) |
||
Строка 20: | Строка 20: | ||
А ответ же на запрос по операции <tex>\oplus</tex> реализуется почти так же, как и в случае без массовых обновлений. Отличие лишь в том, что следует во-первых не забыть раздать детям несогласованность, и во-вторых пересчитать свое значение. | А ответ же на запрос по операции <tex>\oplus</tex> реализуется почти так же, как и в случае без массовых обновлений. Отличие лишь в том, что следует во-первых не забыть раздать детям несогласованность, и во-вторых пересчитать свое значение. | ||
− | + | Рассмотрим описанные выше операции более подробно. В каждом нижеприведенном псевдокоде в узлах дерева хранятся структуры из четырех полей: | |
+ | * <tex>left</tex> {{---}} левая граница полуинтервала, за который "отвечает" текущая вершина. | ||
+ | * <tex>right</tex> {{---}} правая граница этого полуинтервала. | ||
+ | * <tex> ans</tex> {{---}} сумма на отрезке по операции <tex>\oplus</tex>. | ||
+ | * <tex> d</tex> {{---}} несогласованность. | ||
+ | |||
+ | === push === | ||
+ | Так называемое "проталкиванием" несогласованности детям. Необходимо выполнять как только идет рекурсивный запуск от текущей вершины к её детям. Нужно это для того, чтобы в детях в момент обработки были корректные данные. | ||
+ | |||
+ | void push(int node) { | ||
+ | // node - текущая вершина | ||
+ | tree[2 * node + 1].d = tree[2 * node + 1].d <tex>\odot</tex> tree[node].d; | ||
+ | tree[2 * node + 2].d = tree[2 * node + 2].d <tex>\odot</tex> tree[node].d; | ||
+ | tree[node].d = <tex>\perp</tex>; // Нейтральный элемент | ||
+ | } | ||
+ | === update === | ||
+ | Процедура обновления на отрезке. Данная процедура выполняет разбиение текущего отрезка на подотрезки и обновление в них несогласованности. Очень важно выполнить push как только идет рекурсивный вызов от детей, чтобы избежать некорректной обработки в детях. И так как значение в детях могло измениться, то необходимо выполнить обновление ответа по операций <tex>\oplus</tex> на текущем отрезке. | ||
+ | |||
+ | void update(int node, int a, int b, my_type val) { | ||
+ | // val - значение, которое поступило в качестве параметра на запрос, a и b - границы запроса | ||
+ | l = tree[node].left; | ||
+ | r = tree[node].right; | ||
+ | '''if''' [l, r) <tex>\bigcap </tex> [a, b) == <tex> \varnothing</tex> | ||
+ | return; | ||
+ | '''if''' [l, r) <tex>\subset </tex> [a, b) | ||
+ | tree[node].d = tree[node].d <tex>\odot</tex> val; | ||
+ | return; | ||
+ | |||
+ | push(node); | ||
+ | // Обновление детей | ||
+ | update(2 * node + 1, a, b, val); | ||
+ | update(2 * node + 2, a, b, val); | ||
+ | // Пересчет значения на текущем отрезке | ||
+ | tree[node].ans = (tree[2 * node + 1].ans <tex>\odot</tex> tree[2 * node + 1].d) <tex>\oplus</tex> | ||
+ | (tree[2 * node + 2].ans <tex>\odot</tex> tree[2 * node + 2].d); | ||
+ | } | ||
+ | === get_ans === | ||
+ | Получение ответа по операций <tex>\oplus</tex>. Отличие от операций обновления лишь в том, что для каждого отрезка разбиения необходимо не обновить несогласованность, а сложить по операций <tex>\oplus</tex> с текущим ответом истинное значение на отрезке (то есть результат сложения по операций <tex>\odot</tex> значения в вершине с несогласованностью). | ||
+ | |||
+ | my_type get_ans(int node, int a, int b) { | ||
+ | l = tree[node].left; | ||
+ | r = tree[node].right; | ||
+ | '''if''' [l, r )<tex>\bigcap </tex> [a, b) == <tex> \varnothing</tex> | ||
+ | return <tex>\perp</tex>; | ||
+ | '''if''' [l, r) <tex>\subset </tex> [a, b) | ||
+ | return tree[node].ans <tex>\odot</tex> tree[node].d; | ||
+ | push(node); | ||
+ | int ans = get_ans (node * 2 + 1, a, b) <tex>\oplus</tex> | ||
+ | get_ans (node * 2 + 2, a, b)); | ||
+ | tree[node].ans = (tree[2 * node + 1].ans <tex>\odot</tex> tree[2 * node + 1].d) <tex>\oplus</tex> | ||
+ | (tree[2 * node + 2].ans <tex>\odot</tex> tree[2 * node + 2].d); | ||
+ | return ans; | ||
+ | } | ||
==Псевдокод в общем виде== | ==Псевдокод в общем виде== |
Версия 00:23, 9 июня 2012
Дерево отрезков позволяет осуществлять так называемые массовые операции, то есть данная структура позволяет выполнять операции с несколькими подряд идущими элементами. Причем время работы, как и при других запросах, равно .
Содержание
Несогласованные поддеревья
Сперва рассмотрим так называемые несогласованные поддеревья.
Пусть дерево отрезков хранить в вершинах результат выполнение операций
на текущем отрезке, а запрос обновления идет по операций .В несогласованном поддереве дерева отрезков в вершинах хранятся не истинные значения сумм (по операции
) на отрезках, однако гарантируется, что на запрос они отвечают верно. При этом в корне поддерева, которому соответствует отрезок хранится несогласованность . Если в вершине хранится истинное значение суммы, то — нейтральный элемент относительно операции (например 0 для прибавления). Для реализации операция должна быть ассоциативной, и операции должны удовлетворять свойству дистрибутивности:Массовое обновление
Рассмотрим в общем виде реализацию массовой операции на отрезке. Пусть необходимо отвечать запросы относительно операции
, а запрос массового обновления идет по операции .Для эффективной реализаций будем использовать описанную выше структуру — несогласованные поддеревья. В каждой вершине, помимо непосредственно результата выполнения операции
, храним несогласованность — величина, с которой нужно выполнить операцию для всех элементов текущего отрезка. Тем самым мы сможем обрабатывать запрос массового обновления на любом подотрезке эффективно, вместо того чтобы изменять все значений. Как известно из определения несогласованных поддеревьев, в текущий момент времени не в каждой вершине дерева хранится истинное значение, однако когда мы обращаемся к текущему элементу мы работаем с верными данными. Это обеспечивается так называемым "проталкиванием" несогласованности детям (процедура push) при каждом обращений к текущей вершине. При этом после обращения к вершине необходимо пересчитать значение по операции , так как значение в детях могло измениться.А ответ же на запрос по операции
реализуется почти так же, как и в случае без массовых обновлений. Отличие лишь в том, что следует во-первых не забыть раздать детям несогласованность, и во-вторых пересчитать свое значение.Рассмотрим описанные выше операции более подробно. В каждом нижеприведенном псевдокоде в узлах дерева хранятся структуры из четырех полей:
- — левая граница полуинтервала, за который "отвечает" текущая вершина.
- — правая граница этого полуинтервала.
- — сумма на отрезке по операции .
- — несогласованность.
push
Так называемое "проталкиванием" несогласованности детям. Необходимо выполнять как только идет рекурсивный запуск от текущей вершины к её детям. Нужно это для того, чтобы в детях в момент обработки были корректные данные.
void push(int node) { // node - текущая вершина tree[2 * node + 1].d = tree[2 * node + 1].dtree[node].d; tree[2 * node + 2].d = tree[2 * node + 2].d tree[node].d; tree[node].d = ; // Нейтральный элемент }
update
Процедура обновления на отрезке. Данная процедура выполняет разбиение текущего отрезка на подотрезки и обновление в них несогласованности. Очень важно выполнить push как только идет рекурсивный вызов от детей, чтобы избежать некорректной обработки в детях. И так как значение в детях могло измениться, то необходимо выполнить обновление ответа по операций
на текущем отрезке.void update(int node, int a, int b, my_type val) { // val - значение, которое поступило в качестве параметра на запрос, a и b - границы запроса l = tree[node].left; r = tree[node].right; if [l, r)[a, b) == return; if [l, r) [a, b) tree[node].d = tree[node].d val; return; push(node); // Обновление детей update(2 * node + 1, a, b, val); update(2 * node + 2, a, b, val); // Пересчет значения на текущем отрезке tree[node].ans = (tree[2 * node + 1].ans tree[2 * node + 1].d) (tree[2 * node + 2].ans tree[2 * node + 2].d); }
get_ans
Получение ответа по операций
. Отличие от операций обновления лишь в том, что для каждого отрезка разбиения необходимо не обновить несогласованность, а сложить по операций с текущим ответом истинное значение на отрезке (то есть результат сложения по операций значения в вершине с несогласованностью).my_type get_ans(int node, int a, int b) { l = tree[node].left; r = tree[node].right; if [l, r )[a, b) == return ; if [l, r) [a, b) return tree[node].ans tree[node].d; push(node); int ans = get_ans (node * 2 + 1, a, b) get_ans (node * 2 + 2, a, b)); tree[node].ans = (tree[2 * node + 1].ans tree[2 * node + 1].d) (tree[2 * node + 2].ans tree[2 * node + 2].d); return ans; }
Псевдокод в общем виде
Пусть поступают запросы двух типов — поиск элемента, соответствующего операции
, и массовое обновление на отрезке относительно операции . Используется классическая реализация дерева отрезка с полуинтервалами.Пусть в узлах дерева хранятся структуры из четырех полей:
- — левая граница полуинтервала, за который "отвечает" текущая вершина.
- — правая граница этого полуинтервала.
- — сумма на отрезке по операции .
- — несогласованность.
// Процедура "проталкивания" несогласованности детям void push(int node) { tree[2 * node + 1].d = tree[2 * node + 1].dtree[node].d; tree[2 * node + 2].d = tree[2 * node + 2].d tree[node].d; tree[node].d = ; // Нейтральный элемент } // Процедура ответа на множественные запросы. void update(int node, int a, int b, my_type val) { // val - значение, которое поступило в качестве параметра на запрос l = tree[node].left; r = tree[node].right; if [l, r) [a, b) == return; if [l, r) [a, b) tree[node].d = tree[node].d val; return; push(node); // Вызываем обновление детей update(2 * node + 1, a, b, val); update(2 * node + 2, a, b, val); // Пересчитываем свое значение tree[node].ans = (tree[2 * node + 1].ans tree[2 * node + 1].d) (tree[2 * node + 2].ans tree[2 * node + 2].d); } // Функция ответа на запросы my_type get_ans(int node, int a, int b) { // node - текущая вершина, a и b - границы запроса l = tree[node].left; r = tree[node].right; if [l, r ) [a, b) == return ; if [l, r) [a, b) return tree[node].ans tree[node].d; push(node); int ans = get_ans (node * 2 + 1, a, b) get_ans (node * 2 + 2, a, b)); tree[node].ans = (tree[2 * node + 1].ans tree[2 * node + 1].d) (tree[2 * node + 2].ans tree[2 * node + 2].d); return ans; }