Изменения

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

Centroid decomposition

Нет изменений в размере, 15:53, 17 июня 2017
Нет описания правки
}}
Для начала решим обе задачи.
Первая задача решается методом [[Сортировка_слиянием|«разделяй и властвуй»]] {{---}} давайте разделим массив <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}, 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, 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) * (\dfrac{n}{2} - p_1)</math>, после чего увеличим <math>p_1</math> на <math>1</math>. Так будем делать, пока <math>p_1 < \dfrac{n}{2}</math>. В конце сложим текущий ответ и ответы для половин массива {{---}} получим ответ на задачу. Асимптотика такого алгоритма: <tex>T(n) = 2 * T(n / 2) + O(n) = O(n)</tex>
Вторая задача имеет запросы на изменение и поэтому надо применить динамическую версию «разделяй и властвуй» {{---}} [[Дерево_отрезков._Построение|дерево отрезков]]. Построим дерево отрезков,
поддерживающее 2 вида запросов: присвоение в точке и минимум на отрезке. Изначально сделаем так, чтобы дереву отрезков соответствовал массив <math>b</math>, такой что <tex>b_i = 0</tex>, если в i-м городе принимает госпиталь и <tex>b_i = 1</tex> иначе. Когда в каком-то городе открывается/закрывается госпиталь {{---}} делаем запрос на изменение в дереве отрезков. Когда требуется узнать ближайший госпиталь к <math>i</math>-му городу, можно воспользоваться одной из следующих идей:
* (<tex>(O(n \cdot log^2(n))</tex>) Бинарным поиском ищем ближайший слева и ближайший справа к i-му городу госпиталь (такой город <math>j</math>, что <tex>\min\limits_{k=i..j}b_k= 1)</tex>). Для этого внутри бинарного поиска каждый раз делаем запрос на поиск минимума в дереве отрезков. * (<tex>(O(n \cdot log(n))</tex>) Будем одним спуском/подъемом по дереву определять, куда нам нужно идти (в левое или правое поддерево), тем самым делая одновременно и бинарный поиск, и спуск/подъем по дереву.
== Статическая центроидная декомпозиция ==
|id=191213
|definition =
'''Центроидом дерева''' (англ. ''centroid'') называется такая вершина <math>v</math> дерева <math>t</math>, после удаления которой дерево разбивается на несколько (<math>(k)</math>) поддеревьев <tex>t_1, t_2,\dots, t_k</tex>, таких что для каждого <math>i</math>: <tex>|t_i| \leqslant \dfrac{n}{2}</tex>, то есть размер каждого поддерева не превосходит половины размера исходного дерева.
}}
Итак, в случае дерева идея "разделяй и властвуй" из предыдущего пункта будет формулироваться так: [[#l1|найдем центроид]]. Предположим, что мы сумели найти центроид за <math>O(n)</math>, где <math>n</math> {{---}} размер дерева. Тогда, как и в упрощенной версии задачи {{---}} рекурсивно найдем ответ для всех поддеревьев <tex>t_1, t_2,\dots, t_k</tex>, после чего попытаемся найти недостающие пары вершин, находящиеся в разных поддеревьях и удовлетворяющих вопросу задачи. Для этого будем отвечать на следующие запросы: пусть мы сейчас считаем все пары, где первая из вершин находится в поддереве <math>t_i</math> и мы в некоторой структуре данных <math>S</math> храним все вершины остальных деревьев (каждую вершину задаем парой <math>(depth(v), length(v))</math> {{---}} глубина вершины и длина пути до нее из корня поддерева), расстояние до которых от корня их поддерева не превышает <math>min(l, n)</math>. Тогда просто пройдемся по всем вершинам <math>u</math> поддерева <math>t_i</math> и прибавим к ответу число вершин в структуре <math>S</math>, таких, что <tex>depth(u) \leqslant l - depth(v)</tex> и <tex>length(u) \leqslant l - length(v)</tex>. Это двумерные запросы, на которые можно отвечать за <math>O(log^2(n)</math> с помощью [[Многомерное_дерево_отрезков|2d-дерева отрезков]], либо за <math>O(log(n))</math> с помощью [[Перечисление_точек_в_произвольном_прямоугольнике_за_n_*_log_%5E(d_-_1)_n_(range_tree)|техники поиска точек в d-мерном пространстве]]. Также читателю предлагается придумать и более эффективные и простые способы решить эту подзадачу.
В любом дереве <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> и продолжим выбор нового u, иначе {{---}} остановимся. Докажем, что мы в какой-то момент остановимся. Пусть в произвольный момент времени <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>). По индукции получаем, что в любой момент времени размер наддерева вершины v меньше <math>\dfrac{n}{2}</math>, значит мы будем спускаться только вниз по дереву <math>t</math>, и при переходе к вершине <math>u</math> {{---}} сыну <math>v</math> размер максимального поддерева уменьшится как минимум на <math>1</math>. Значит не более чем за <math>n</math> шагов наши действия прекратятся и мы окажемся в центроиде дерева <math>t</math>.
Итак, мы конструктивно доказали существование центроида и привели линейный относительно размера дерева алгоритм его нахождения.
}}
# Третье свойство {{---}} прямое следствие первых двух, т.к. вершина принадлежит любому центроиду <math>c</math> т.и т.т., когда c {{---}} отец вершины <math>v</math> в дереве центроидов. Т.к. вершина <math>v</math> точно принадлежит дереву <math>T</math> (свойство 2), то она лежит на каком-то пути в дереве <math>T</math>, причем все ее родители (центроиды) ее содержат. А по свойству 1 длина любого вертикального (и даже простого) пути есть <math>O(log(n))</math>.
# Четвертое свойство очевидно из того, что <math>T</math> {{---}} дерево. Т.к. <math>T(u)</math> и <math>T(v)</math> {{---}} поддеревья различных вершин дерева <math>T</math>, то либо они не пересекаются, либо <math>u</math> {{---}} предок <math>v</math>, и значит <tex>T(v) \subset T(u)</tex>, либо <math>v</math> {{---}} предок <math>u</math>, и значит <tex>T(u) \subset T(v)</tex>.
# Для доказательства последнего свойства выберем в качестве вершины <math>c</math> <math>lca(u, v)</math> в дереве центроидов <math>T</math>. Покажем, что так выбранная вершина <math>c</math> удовлетворяет заявленным свойствам. То, что <tex>u, v \in T(c)</tex> {{---}} очевидно по определению <math>lca</math>, т.к. каждый предок любой вершины в дереве центроидов содержит эту вершину. Теперь докажем, что <math>c</math> лежит на пути между парой вершин <math>u, v</math>. Т.к. <math>c = lca(u, v)</math> в <math>T</math>, то из <math>c</math> нет ребра в такого сына, который содержит одновременно <math>u</math> и <math>v</math> в своем поддереве (в дереве <math>T)</math>), значит после удаления <math>c</math> дерево <math>t</math> разделится на несколько поддеревьев, таких что вершины <math>u</math> и <math>v</math> окажутся в разных компонентах связности. А значит найдется такое ребро <math>(c, x)</math>, которое принадлежало пути из <math>u</math> в <math>v</math>, но после удаления <math>c</math> удалилось. Это доказывает то, что вершина <math>c</math> лежала на пути из <math>u</math> в <math>v</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>. Если изначально предпосчитать проходом [[Обход_в_глубину,_цвета_вершин| dfs]] величины <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> помеченной вершины {{---}} это запрос поиска вершины u, такой что величина <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(n)</math> дополнительной памятью и временной сложностью <math>O(log^2(n))</math> на запрос любого типа с предварительным предпосчетом за <math>O(n)</math>.
* Для каждой вершины <math>v</math> исходного дерева запомним величину <math>p_v</math> {{---}} номер предка вершины <math>v</math> в дереве <math>T</math>.
Этот подход наиболее экономный по памяти (<math>(O(n))</math>), но уступает в скорости и функциональности.
* Для каждой вершины <math>v</math> исходного дерева будем хранить весь массив предков <math>p[v]</math> в дереве центроидов.
Этот подход уступает в количестве необходимой дополнительной памяти (<tex>(O(n \cdot log(n))</tex> суммарно), но имеет ряд преимуществ:
#При проходе по массиву предков фиксированной вершины будет выигрыш в скорости работы, т.к. весь массив будет лежать непрерывным блоком данных и следовательно будет закэширован
Анонимный участник

Навигация