Изменения

Перейти к: навигация, поиск

Centroid decomposition

45 байт добавлено, 18:36, 8 сентября 2019
Динамическая центроидная декомпозиция (дерево центроидной декомпозиции)
Centroid decomposition Центроидная декомпозиция (русангл. центроидная декомпозиция''centroid decomposition'') {{---}} это структура данных, позволяющая отвечать на запросы на дереве. Чаще всего это запросы, связанные с нахождением функции на вершинах, связанных неравенством на расстояние между ними в дереве. Также иногда применяется для запросов на путях в дереве.
== Введение ==
Рассмотрим <math>2</math> задачи на обычном массиве (в дальнейшем мы будем их обобщать на случай дерева):
Задача <math>1</math>:
{{Задача
|definition = Есть массив <tex>a</tex> положительных целых чисел из <tex>n</tex> элементов. Также дано число <tex>W \geqslant 0</tex> и число <tex>l</tex>. Требуется найти количество пар <tex>(i, j)</tex> индексов массива, таких что <tex>|j - i| \leqslant l </tex> и <tex>\sum\limits_{k=i}^{j} a_k \leqslant W</tex>.
Первая задача решается методом [[Сортировка_слиянием|«разделяй и властвуй»]] {{---}} давайте разделим массив <tex>a[0 \dots n-1]</tex> на 2 массива <tex>a[0\dots\dfrac{n}{2} - 1]</tex> и <tex>a[\dfrac{n}{2} \dots n-1]</tex> и рекурсивно решим задачу для каждого из них. Осталось научиться находить количество искомых пар <tex>(i, j)</tex>, таких что <tex>i < \dfrac{n}{2},\text{ }j \geqslant \dfrac{n}{2}</tex>. Для этого воспользуемся другой известной техникой {{---}} методом двух указателей. Посчитаем массив префиксных сумм для правой половины <tex>pref[i] = \sum\limits_{j=\frac{n}{2}}^{i} a_j</tex> и суффиксных <tex>(suf[i] = \sum\limits_{j=i}^{\frac{n}{2} + 1} a_j)</tex> {{---}} для левой. Заведем два указателя <tex>(p_1</tex> и <tex>p_2)</tex>. Изначально установим <tex>p_1 = \dfrac{n}{2} - l + 1,\text{ }p_2 = \dfrac{n}{2}</tex>. Пока <tex>p_2 - 1> \dfrac{n}{2}</tex> и
<tex>pref[p_2] + suf[p_1] > W </tex> будем уменьшать <math>p_2</math> на <math>1</math>. Если после этого <math>pref[p_2] + suf[p_1] \leqslant W</math>, то к ответу прибавим <math>(p_2 - \dfrac{n}{2} + 1) \cdot (\dfrac{n}{2} - p_1)</math>, после чего увеличим <math>p_1</math> на <math>1</math>. Так будем делать, пока <math>p_1 < \dfrac{n}{2}</math>. В конце сложим текущий ответ и ответы для половин массива {{---}} получим ответ на задачу. Время работы такого алгоритма: <tex>T(n) = 2 \cdot T(n / 2) + O(n) = O(n*log(n))</tex>
Вторая задача имеет запросы на изменение и поэтому надо применить динамическую версию «разделяй и властвуй» {{---}} [[Дерево_отрезков._Построение|дерево отрезков]]. Построим дерево отрезков,
В любом дереве <math>t</math> существует центроид.
|proof=
Рассмотрим корень дерева <math>(r)</math>. Положим изначально <math>v = r</math>. Изначально <math>|subtree(v)| = n</math>. Среди всех детей <math>v</math> выберем вершину <math>u</math> с максимальным размером поддерева. Если <math>v</math> {{---}} не центроид, то положим <math>v = u</math> и продолжим выбор нового <math>u</math>, иначе {{---}} остановимся. Докажем, что мы в какой-то момент остановимся. Пусть в произвольный момент времени <math>v</math> {{---}} не центроид и размер её наддерева меньше <math>\dfrac{n}{2}</math>, значит максимальное поддерево имеет размер больше чем <math>\dfrac{n}{2}</math>, то есть <math>|subtree(u)| > \dfrac{n}{2}</math>, а значит размер "наддерева" вершины <math>u</math> равен <tex>n - |subtree(u)| < \dfrac{n}{2}</tex>. При этом теперь размер любого поддерева, на которое распадется дерево t при удалении вершины <math>u</math> не превосходит <math>|subtree(u)| - 1</math>, так как наддерево имеет размер меньше, чем поддерево <math>u</math>, а любое поддерево вершины <math>u</math> имеет хотя бы на <math>1</math> вершину меньше (сама вершина <math>u)</math>. По индукции получаем, что в любой момент времени размер наддерева вершины <math>v </math> меньше <math>\dfrac{n}{2}</math>, значит мы будем спускаться только вниз по дереву <math>t</math>, и при переходе к вершине <math>u</math> {{---}} сыну <math>v</math> размер максимального поддерева уменьшится как минимум на <math>1</math>. Значит не более чем за <math>n</math> шагов наши действия прекратятся и мы окажемся в центроиде дерева <math>t</math>.
Итак, мы конструктивно доказали существование центроида и привели линейный относительно размера дерева алгоритм его нахождения.
}}
{{Определение
|definition=
'''Деревом центроидов ''' (или центроидной декомпозицией, англ. ''centroid decomposition tree'')’’' дерева <math>t</math> называют дерево <math>T(t)</math>, построенное на вершинах дерева <math>t</math> следующим образом:
* Пусть вершина <math>c</math> {{---}} центроид дерева <math>t</math>. Объявим его корнем нового дерева <math>T</math>.
* Пусть при удалении вершины <math>c</math> из дерева <math>t</math> оно распалось на поддеревья <tex>t_1, t_2,\dots, t_k</tex>, тогда детьми вершины c объявим <tex>T(t_1), T(t_tt_2),\dots, T(t_k)</tex>.
}}
=== Свойства центроидной декомпозиции ===
}}
С помощью описанных свойств дерева <math>T</math> мы можем решить задачу <math>2 </math> для дерева:
=== Пример решения задачи с помощью центроидной декомпозиции ===
==== Решение ====
Построим центроидную декомпозицию <math>T</math> дерева <math>t</math>. Изначально посчитаем для каждого центроида, содержащего вершину <math>0</math> посчитаем расстояние до вершины <math>0</math>. Для этого воспользуемся [[Метод_двоичного_подъема|методом двоичных подъемов для поиска lca пары вершин в дереве]], а также тем фактом, что если глубина <math>h(v)</math> вершины <math>v</math> в дереве определена как расстояние от корня до вершины <math>v</math>, то длина пути между парой вершин <math>(u, v)</math> есть <tex>len(u, v) = h(u) + h(v) - 2 \cdot h(lca(u, v))</tex>. Если изначально предпосчитать проходом [[Обход_в_глубину,_цвета_вершин| <math>dfs</math>]] величины <math>h(v)</math> за <math>O(n)</math>, то ответ на запрос <math>len(u, v)</math> можно делать за время <math>O(\log(n))</math> с <tex>O(n \cdot \log(n)) </tex>доп. памятью. Также можно воспользоваться техникой [[Сведение_задачи_LCA_к_задаче_RMQ|сведения задачи LCA к RMQ]] и решить с <math>O(n)</math> дополнительной памятью и <math>O(1)</math> времени на запрос. Теперь научимся отвечать на запросы. Из последнего свойства центроидной декомпозиции видно, что если <math>u</math> {{---}} искомая ближайшая помеченная вершина к <math>v</math>, то путь между ними содержит центроид <math>c</math>, такой что <tex>u, v \in T(c)</tex>, причем <math>c</math> {{---}} предок одновременно вершин <math>u, v</math> в дереве<math>T</math>. Поэтому заведем [[Красно-черное_дерево|двоичное дерево поиска]] для каждого центроида <math>c \in T</math>. В этой структуре для каждой вершины <math>c</math> будем хранить пары <math>(len(c, u), u)</math> для всех помеченных вершин <math>u</math> в поддереве центроидов <math>T(c)</math>. Когда приходит запрос пометить вершину <math>v</math> {{---}} добавим в структуру данных для всех предков <math>p_c</math> вершины <math>v</math> в дереве <math>T</math> пары <math>(len(p_c, v), v)</math>. Мы совершим <math>O(\log(n))</math> добавлений, затратив <math>O(\log(n))</math> действий на каждое. Запрос снятия пометки с вершины обрабатывается аналогичными удалениями. Запрос поиска ближайшей к <math>v</math> помеченной вершины {{---}} это запрос поиска вершины <math>u</math>, такой что величина <math>len(c, u) + len(c, v)</math> минимальна, где <math>c</math> {{---}} предок <math>v</math> в дереве центроидов (по пятому свойству, нас интересуют именно такие <math>c)</math>. Этот запрос занимает так же <tex>O(log^2(n))</tex> времени.
#При проходе по массиву предков фиксированной вершины будет выигрыш в скорости работы, так как весь массив будет лежать непрерывным блоком данных и следовательно будет закэширован
# На массиве предков можно строить различные структуры данных (такие как, например, дерево отрезков) для быстрого (в случае с деревом отрезков <math>O(log(\log(n)))</math> на запрос) поиска предка с необходимыми свойствами. Так, например, в описанной выше задаче про помеченные вершины наибольшего общего предка можно искать методом двоичных подъемов за <math>O(\log(\log(n))</math> на запрос, так как размер массива предков есть <math>O(\log(n))</math> (по свойству <math>1</math> центроидной декомпозиции). Используя эту оптимизацию можно получить время <math>O(\log(n)) + O(\log(\log(n)) = O(\log(n))</math> на запрос нахождения ближайшей помеченной вершины. Чтобы добиться улучшенной асимптотики для запросов изменения можно хранить дерево отрезков на каждом из путей <math>p[v]</math>, в каждой вершине которого хранить двоичное дерево поиска и поддерживать отложенные операции. Тогда ответ на эти запросы будет занимать <math>O(\log(n) \cdot \log(\log(n))</math> времени.
==См. также==
Анонимный участник

Навигация