<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ru">
		<id>http://neerc.ifmo.ru/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=92.255.113.52&amp;*</id>
		<title>Викиконспекты - Вклад участника [ru]</title>
		<link rel="self" type="application/atom+xml" href="http://neerc.ifmo.ru/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=92.255.113.52&amp;*"/>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%BB%D1%83%D0%B6%D0%B5%D0%B1%D0%BD%D0%B0%D1%8F:%D0%92%D0%BA%D0%BB%D0%B0%D0%B4/92.255.113.52"/>
		<updated>2026-06-11T20:00:06Z</updated>
		<subtitle>Вклад участника</subtitle>
		<generator>MediaWiki 1.30.0</generator>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61842</id>
		<title>Centroid decomposition</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61842"/>
				<updated>2017-06-25T13:56:13Z</updated>
		
		<summary type="html">&lt;p&gt;92.255.113.52: /* Варианты хранения центроидной декомпозиции */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Центроидная декомпозиция (англ. ''centroid decomposition'') {{---}} это структура данных, позволяющая отвечать на запросы на дереве. Чаще всего это запросы, связанные с нахождением функции на вершинах, связанных неравенством на расстояние между ними в дереве. Также иногда применяется для запросов на путях в дереве.&lt;br /&gt;
&lt;br /&gt;
== Введение ==&lt;br /&gt;
Рассмотрим &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; задачи на обычном массиве (в дальнейшем мы будем их обобщать на случай дерева):&lt;br /&gt;
&lt;br /&gt;
Задача &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть массив &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; положительных целых чисел из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов. Также дано число &amp;lt;tex&amp;gt;W \geqslant 0&amp;lt;/tex&amp;gt; и число &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; индексов массива, таких что &amp;lt;tex&amp;gt;|j - i| \leqslant l &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\sum\limits_{k=i}^{j} a_k \leqslant W&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
Задача &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть прямая дорога, на которой расположены &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; городов. В некоторых городах есть госпитали, которые могут принимать больных. Поступают запросы вида:&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;, в котором находится больной и требуется найти такой город &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, который может принимать больных и &amp;lt;tex&amp;gt;|u - v|&amp;lt;/tex&amp;gt; минимально возможное.&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что больше он не будет принимать больных&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что теперь он может принимать больных&lt;br /&gt;
}}&lt;br /&gt;
Для начала решим обе задачи. &lt;br /&gt;
Первая задача решается методом [[Сортировка_слиянием|«разделяй и властвуй»]] {{---}} давайте разделим массив &amp;lt;tex&amp;gt;a[0 \dots n-1]&amp;lt;/tex&amp;gt; на 2 массива &amp;lt;tex&amp;gt;a[0\dots\dfrac{n}{2} - 1]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;a[\dfrac{n}{2} \dots n-1]&amp;lt;/tex&amp;gt; и рекурсивно решим задачу для каждого из них. Осталось научиться находить количество искомых пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt;, таких что &amp;lt;tex&amp;gt;i &amp;lt; \dfrac{n}{2},\text{ }j \geqslant \dfrac{n}{2}&amp;lt;/tex&amp;gt;. Для этого воспользуемся другой известной техникой {{---}} методом двух указателей. Посчитаем массив префиксных сумм для правой половины &amp;lt;tex&amp;gt;pref[i] = \sum\limits_{j=\frac{n}{2}}^{i} a_j&amp;lt;/tex&amp;gt; и суффиксных &amp;lt;tex&amp;gt;(suf[i] = \sum\limits_{j=i}^{\frac{n}{2} + 1} a_j)&amp;lt;/tex&amp;gt; {{---}} для левой. Заведем два указателя &amp;lt;tex&amp;gt;(p_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;p_2)&amp;lt;/tex&amp;gt;. Изначально установим &amp;lt;tex&amp;gt;p_1 = \dfrac{n}{2} - l + 1,\text{ }p_2 = \dfrac{n}{2}&amp;lt;/tex&amp;gt;. Пока &amp;lt;tex&amp;gt;p_2 - 1&amp;gt; \dfrac{n}{2}&amp;lt;/tex&amp;gt; и&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;pref[p_2] + suf[p_1] &amp;gt; W &amp;lt;/tex&amp;gt; будем уменьшать &amp;lt;math&amp;gt;p_2&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Если после этого &amp;lt;math&amp;gt;pref[p_2] + suf[p_1] \leqslant W&amp;lt;/math&amp;gt;, то к ответу прибавим &amp;lt;math&amp;gt;(p_2 - \dfrac{n}{2} + 1) \cdot (\dfrac{n}{2} - p_1)&amp;lt;/math&amp;gt;, после чего увеличим &amp;lt;math&amp;gt;p_1&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Так будем делать, пока &amp;lt;math&amp;gt;p_1 &amp;lt; \dfrac{n}{2}&amp;lt;/math&amp;gt;. В конце сложим текущий ответ и ответы для половин массива {{---}} получим ответ на задачу. Время работы такого алгоритма: &amp;lt;tex&amp;gt;T(n) = 2 \cdot T(n / 2) + O(n) = O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вторая задача имеет запросы на изменение и поэтому надо применить динамическую версию «разделяй и властвуй» {{---}} [[Дерево_отрезков._Построение|дерево отрезков]]. Построим дерево отрезков,&lt;br /&gt;
поддерживающее 2 вида запросов: присвоение в точке и минимум на отрезке. Изначально сделаем так, чтобы дереву отрезков соответствовал массив &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;b_i = 0&amp;lt;/tex&amp;gt;, если в &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м городе принимает госпиталь и &amp;lt;tex&amp;gt;b_i = 1&amp;lt;/tex&amp;gt; иначе. Когда в каком-то городе открывается/закрывается госпиталь {{---}} делаем запрос на изменение в дереве отрезков. Когда требуется узнать ближайший госпиталь к &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-му городу, можно воспользоваться одной из следующих идей: &lt;br /&gt;
* &amp;lt;tex&amp;gt;(O(n \cdot log^2(n))&amp;lt;/tex&amp;gt; Бинарным поиском ищем ближайший слева и ближайший справа к &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-му городу госпиталь (такой город &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, что &amp;lt;tex&amp;gt;\min\limits_{k=i..j}b_k= 1)&amp;lt;/tex&amp;gt;. Для этого внутри бинарного поиска каждый раз делаем запрос на поиск минимума в дереве отрезков.  &lt;br /&gt;
* &amp;lt;tex&amp;gt;(O(n \cdot \log(n))&amp;lt;/tex&amp;gt; Будем одним спуском/подъемом по дереву определять, куда нам нужно идти (в левое или правое поддерево), тем самым делая одновременно и бинарный поиск, и спуск/подъем по дереву.&lt;br /&gt;
&lt;br /&gt;
== Статическая центроидная декомпозиция ==&lt;br /&gt;
Перейдем к обобщению поставленных задач на случай дерева.&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть взвешенное дерево &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин, в каждой вершине которого написаны положительные целые числа. Также по-прежнему даны числа &amp;lt;tex&amp;gt;W \geqslant 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; вершин дерева, таких что расстояние между ними не превосходит &amp;lt;math&amp;gt;l&amp;lt;/math&amp;gt; по числу ребер и не превосходит &amp;lt;math&amp;gt;W&amp;lt;/math&amp;gt; по сумме весов&amp;lt;ref&amp;gt;[http://neerc.ifmo.ru/school/camp-2016/problems/20160324a1.pdf  Сайт с задачами Санкт-Петербургских сборов к РОИ 2016]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Для решения новой задачи применим ту же идею, что и была до этого {{---}} «разделяй и властвуй». Для этого нам потребуется следующий объект:&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|id=191213&lt;br /&gt;
|definition =&lt;br /&gt;
'''Центроидом дерева''' (англ. ''centroid'') называется такая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, после удаления которой дерево разбивается на несколько &amp;lt;math&amp;gt;(k)&amp;lt;/math&amp;gt; поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, таких что для каждого &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;: &amp;lt;tex&amp;gt;|t_i| \leqslant \dfrac{n}{2}&amp;lt;/tex&amp;gt;, то есть размер каждого поддерева не превосходит половины размера исходного дерева.&lt;br /&gt;
}}&lt;br /&gt;
Итак, в случае дерева идея «разделяй и властвуй» из предыдущего пункта будет формулироваться так: [[#l1|найдем центроид]]. Предположим, что мы сумели найти центроид за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; {{---}} размер дерева. Тогда, как и в упрощенной версии задачи {{---}} рекурсивно найдем ответ для всех поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, после чего попытаемся найти недостающие пары вершин, находящиеся в разных поддеревьях и удовлетворяющих вопросу задачи. Для этого будем отвечать на следующие запросы: пусть мы сейчас считаем все пары, где первая из вершин находится в поддереве &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и мы в некоторой структуре данных &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; храним все вершины остальных деревьев (каждую вершину задаем парой &amp;lt;math&amp;gt;(depth(v), length(v))&amp;lt;/math&amp;gt; {{---}} глубина вершины и длина пути до нее из корня поддерева), расстояние до которых от корня их поддерева не превышает &amp;lt;math&amp;gt;min(l, n)&amp;lt;/math&amp;gt;. Тогда просто пройдемся по всем вершинам &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; поддерева &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и прибавим к ответу число вершин в структуре &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt;, таких, что &amp;lt;tex&amp;gt;depth(u) \leqslant l - depth(v)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;length(u) \leqslant l - length(v)&amp;lt;/tex&amp;gt;. Это двумерные запросы, на которые можно отвечать за &amp;lt;math&amp;gt;O(log^2(n)&amp;lt;/math&amp;gt; с помощью [[Многомерное_дерево_отрезков|2d-дерева отрезков]], либо за &amp;lt;math&amp;gt;O(\log(n))&amp;lt;/math&amp;gt; с помощью [[Перечисление_точек_в_произвольном_прямоугольнике_за_n_*_log_%5E(d_-_1)_n_(range_tree)|техники поиска точек в d-мерном пространстве]]. Также читателю предлагается придумать и более эффективные и простые способы решить эту подзадачу.&lt;br /&gt;
&lt;br /&gt;
Оценим итоговое время работы алгоритма: &amp;lt;tex&amp;gt;T(n) = k \cdot T(n / k) + O(n \cdot \log(n))&amp;lt;/tex&amp;gt;. Решая это рекуррентное соотношение, получим &amp;lt;math&amp;gt;O(n \cdot \log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь докажем лемму о существовании центроида и опишем алгоритм его эффективного поиска.&lt;br /&gt;
&lt;br /&gt;
=== Лемма о существовании центроида и алгоритм его нахождения. ===&lt;br /&gt;
{{Лемма|id=l1&lt;br /&gt;
|statement=&lt;br /&gt;
В любом дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; существует центроид.&lt;br /&gt;
|proof=&lt;br /&gt;
Рассмотрим корень дерева &amp;lt;math&amp;gt;(r)&amp;lt;/math&amp;gt;. Положим изначально &amp;lt;math&amp;gt;v = r&amp;lt;/math&amp;gt;. Изначально &amp;lt;math&amp;gt;|subtree(v)| = n&amp;lt;/math&amp;gt;. Среди всех детей &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; выберем вершину &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; с максимальным размером поддерева. Если &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид, то положим &amp;lt;math&amp;gt;v = u&amp;lt;/math&amp;gt; и продолжим выбор нового &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, иначе {{---}} остановимся. Докажем, что мы в какой-то момент остановимся. Пусть в произвольный момент времени &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид и размер её наддерева меньше &amp;lt;math&amp;gt;\dfrac{n}{2}&amp;lt;/math&amp;gt;, значит максимальное поддерево имеет размер больше чем &amp;lt;math&amp;gt;\dfrac{n}{2}&amp;lt;/math&amp;gt;, то есть &amp;lt;math&amp;gt;|subtree(u)| &amp;gt; \dfrac{n}{2}&amp;lt;/math&amp;gt;, а значит размер &amp;quot;наддерева&amp;quot; вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; равен &amp;lt;tex&amp;gt;n - |subtree(u)| &amp;lt; \dfrac{n}{2}&amp;lt;/tex&amp;gt;. При этом теперь размер любого поддерева, на которое распадется дерево t при удалении вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; не превосходит &amp;lt;math&amp;gt;|subtree(u)| - 1&amp;lt;/math&amp;gt;, так как наддерево имеет размер меньше, чем поддерево &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, а любое поддерево вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; имеет хотя бы на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; вершину меньше (сама вершина &amp;lt;math&amp;gt;u)&amp;lt;/math&amp;gt;. По индукции получаем, что в любой момент времени размер наддерева вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; меньше &amp;lt;math&amp;gt;\dfrac{n}{2}&amp;lt;/math&amp;gt;, значит мы будем спускаться только вниз по дереву &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, и при переходе к вершине &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} сыну &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; размер максимального поддерева уменьшится как минимум на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Значит не более чем за &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; шагов наши действия прекратятся и мы окажемся в центроиде дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;.&lt;br /&gt;
Итак, мы конструктивно доказали существование центроида и привели линейный относительно размера дерева алгоритм его нахождения. &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Реализация ===&lt;br /&gt;
&lt;br /&gt;
Поиск центроида в дереве:&lt;br /&gt;
&lt;br /&gt;
  '''int''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int[]''' sz)&lt;br /&gt;
     max_subtree = -1&lt;br /&gt;
     '''for''' u : children(v)&lt;br /&gt;
          '''if''' sz[u] &amp;gt; sz[v] / 2 '''and''' sz[u] &amp;gt; sz[max_subtree] &amp;lt;font color=green&amp;gt;// считаем sz[-1] = 0 &amp;lt;/font &amp;gt;&lt;br /&gt;
               max_subtree = u&lt;br /&gt;
     '''if''' max_subtree == -1&lt;br /&gt;
          return v&lt;br /&gt;
     '''else''' &lt;br /&gt;
          '''return''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz)&lt;br /&gt;
&lt;br /&gt;
Шаблон решения произвольной задачи на статическую центроидную декомпозицию:&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int[]''' sz)&lt;br /&gt;
     c = &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz) &amp;lt;font color=green&amp;gt;// находим c {{---}} центроид поддерева вершины v &amp;lt;/font &amp;gt;&lt;br /&gt;
     ch = children[c]&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{deleteEdges}&amp;lt;/tex&amp;gt;(c, children) &amp;lt;font color=green&amp;gt;// удаляем все ребра между вершиной с и детьми, чтобы мы не смогли из детей попасть в с. &lt;br /&gt;
         Это полезно делать, если решение подзадачи для поддерева предполагает проход &amp;lt;math&amp;gt;dfs&amp;lt;/math&amp;gt; &amp;lt;/font &amp;gt;&lt;br /&gt;
     '''for''' c2 : ch[c]&lt;br /&gt;
          &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;(children, c2, sz)&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{mergeSolution}&amp;lt;/tex&amp;gt;(children, c2) &amp;lt;font color=green&amp;gt;// решаем для текущего поддерева &amp;lt;/font &amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Динамическая центроидная декомпозиция (дерево центроидной декомпозиции) ==&lt;br /&gt;
Теперь вернемся ко второй задаче введения. Для ее решения мы пользовались динамичекой версией «разделяй и властвуй» {{---}} деревом отрезков. В предыдущем пункте мы определили статический аналог «разделяй и властвуй» для дерева. Теперь обобщим этот метод для динамических задач.&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Деревом центроидов''' (или центроидной декомпозицией, англ. ''centroid decomposition tree'') дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; называют дерево &amp;lt;math&amp;gt;T(t)&amp;lt;/math&amp;gt;, построенное на вершинах дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; следующим образом:&lt;br /&gt;
* Пусть вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} центроид дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Объявим его корнем нового дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пусть при удалении вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; из дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; оно распалось на поддеревья &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, тогда детьми вершины c объявим &amp;lt;tex&amp;gt;T(t_1), T(t_t),\dots, T(t_k)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
=== Свойства центроидной декомпозиции ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
'''Свойства центроидной декомпозиции''':&lt;br /&gt;
# Глубина дерева центроидов не превосходит &amp;lt;tex&amp;gt;\\log(n)&amp;lt;/tex&amp;gt;. &lt;br /&gt;
# Каждая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; является центроидом одного из поддеревьев дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждая вершина дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; принадлежит &amp;lt;math&amp;gt;O(\log(n))&amp;lt;/math&amp;gt; поддеревьям дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (еще говорят, что вершина принадлежит &amp;lt;math&amp;gt;O(\log(n))&amp;lt;/math&amp;gt; центроидам дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, или что эти центроиды содержат вершину)&lt;br /&gt;
# Для любых вершин &amp;lt;tex&amp;gt;u, v \in T (u \neq v)&amp;lt;/tex&amp;gt; верно ровно одно из следующих трех утверждений: &lt;br /&gt;
#*&amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;&lt;br /&gt;
#* &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;&lt;br /&gt;
#* &amp;lt;tex&amp;gt;T(u) \cap T(v) = \emptyset &amp;lt;/tex&amp;gt;&lt;br /&gt;
# Простой путь между любой парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; содержит центроид &amp;lt;tex&amp;gt;c \in T(t)&amp;lt;/tex&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
# Действительно, так как размер поддерева &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; не превосходит &amp;lt;tex&amp;gt;\dfrac{|subtree(c)|}{2}&amp;lt;/tex&amp;gt;, то при спуске в каждую следующую вершину на пути к любому листу в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; размер поддерева вершины, в которой мы сейчас находимся, уменьшается как минимум на &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;. Значит длина всего пути до листа не превосходит &amp;lt;math&amp;gt;\log(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Второе свойство очевидно из построения дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, так как если вершина принадлежит дереву центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то она является центроидом, а из построения &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы знаем, что каждая вершина исходного дерева принадлежит и дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Третье свойство {{---}} прямое следствие первых двух, так как вершина принадлежит любому центроиду &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; т.и т.т., когда &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} отец вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов. Так как вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; точно принадлежит дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (свойство &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;), то она лежит на каком-то пути в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, причем все ее родители (центроиды) ее содержат. А по свойству &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; длина любого вертикального (и даже простого) пути есть &amp;lt;math&amp;gt;O(\log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Четвертое свойство очевидно из того, что &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; {{---}} дерево. Так как &amp;lt;math&amp;gt;T(u)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;T(v)&amp;lt;/math&amp;gt; {{---}} поддеревья различных вершин дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то либо они не пересекаются, либо &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;, либо &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Для доказательства последнего свойства в качестве вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; выберем &amp;lt;math&amp;gt;lca(u, v)&amp;lt;/math&amp;gt; в дереве центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Покажем, что так выбранная вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удовлетворяет заявленным свойствам. То, что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt; {{---}} очевидно по определению &amp;lt;math&amp;gt;lca&amp;lt;/math&amp;gt;, так как каждый предок любой вершины в дереве центроидов содержит эту вершину. Теперь докажем, что &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежит на пути между парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt;. Так как &amp;lt;math&amp;gt;c = lca(u, v)&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то из &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; нет ребра в такого сына, который содержит одновременно &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в своем поддереве (в дереве &amp;lt;math&amp;gt;T)&amp;lt;/math&amp;gt;, значит после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; разделится на несколько поддеревьев, таких что вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; окажутся в разных компонентах связности. А значит найдется такое ребро &amp;lt;math&amp;gt;(c, x)&amp;lt;/math&amp;gt;, которое принадлежало пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, но после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удалилось. Это доказывает то, что вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежала на пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
С помощью описанных свойств дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы можем решить задачу &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; для дерева:&lt;br /&gt;
&lt;br /&gt;
=== Пример решения задачи с помощью центроидной декомпозиции === &lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин. В каждый момент времени любая вершина дерева может быть либо помечена, либо нет. Изначально помечена только вершина номер &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Дан список из &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; запросов: &lt;br /&gt;
* Вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; пометили.&lt;br /&gt;
* С вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; сняли пометку.&lt;br /&gt;
* Найти номер ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины&amp;lt;ref&amp;gt;[http://neerc.ifmo.ru/school/camp-2016/problems/20160324a1.pdf  Сайт с задачами Санкт-Петербургских сборов к РОИ 2016]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Решение ====&lt;br /&gt;
&lt;br /&gt;
Построим центроидную декомпозицию &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Изначально посчитаем для каждого центроида, содержащего вершину &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt; посчитаем расстояние до вершины &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Для этого воспользуемся [[Метод_двоичного_подъема|методом двоичных подъемов для поиска lca пары вершин в дереве]], а также тем фактом, что если глубина &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве определена как расстояние от корня до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то длина пути между парой вершин &amp;lt;math&amp;gt;(u, v)&amp;lt;/math&amp;gt; есть &amp;lt;tex&amp;gt;len(u, v) = h(u) + h(v) - 2 \cdot h(lca(u, v))&amp;lt;/tex&amp;gt;. Если изначально предпосчитать проходом [[Обход_в_глубину,_цвета_вершин| &amp;lt;math&amp;gt;dfs&amp;lt;/math&amp;gt;]] величины &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, то ответ на запрос &amp;lt;math&amp;gt;len(u, v)&amp;lt;/math&amp;gt; можно делать за время &amp;lt;math&amp;gt;O(\log(n))&amp;lt;/math&amp;gt; с &amp;lt;tex&amp;gt;O(n \cdot \log(n)) &amp;lt;/tex&amp;gt;доп. памятью. Также можно воспользоваться техникой [[Сведение_задачи_LCA_к_задаче_RMQ|сведения задачи LCA к RMQ]] и решить с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; времени на запрос. Теперь научимся отвечать на запросы. Из последнего свойства центроидной декомпозиции видно, что если &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} искомая ближайшая помеченная вершина к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то путь между ними содержит центроид &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;, причем &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок одновременно вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве&amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Поэтому заведем [[Красно-черное_дерево|двоичное дерево поиска]] для каждого центроида &amp;lt;math&amp;gt;c \in T&amp;lt;/math&amp;gt;. В этой структуре для каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; будем хранить пары &amp;lt;math&amp;gt;(len(c, u), u)&amp;lt;/math&amp;gt; для всех помеченных вершин &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в поддереве центроидов &amp;lt;math&amp;gt;T(c)&amp;lt;/math&amp;gt;. Когда приходит запрос пометить вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} добавим в структуру данных для всех предков &amp;lt;math&amp;gt;p_c&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; пары &amp;lt;math&amp;gt;(len(p_c, v), v)&amp;lt;/math&amp;gt;. Мы совершим &amp;lt;math&amp;gt;O(\log(n))&amp;lt;/math&amp;gt; добавлений, затратив &amp;lt;math&amp;gt;O(\log(n))&amp;lt;/math&amp;gt; действий на каждое. Запрос снятия пометки с вершины обрабатывается аналогичными удалениями. Запрос поиска ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины {{---}} это запрос поиска вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, такой что  величина &amp;lt;math&amp;gt;len(c, u) + len(c, v)&amp;lt;/math&amp;gt; минимальна, где &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов (по пятому свойству, нас интересуют именно такие &amp;lt;math&amp;gt;c)&amp;lt;/math&amp;gt;. Этот запрос занимает так же &amp;lt;tex&amp;gt;O(log^2(n))&amp;lt;/tex&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
Итак, мы научились решать задачу с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и временной сложностью &amp;lt;math&amp;gt;O(log^2(n))&amp;lt;/math&amp;gt; на запрос любого типа с предварительным предпосчетом за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Варианты хранения центроидной декомпозиции ==&lt;br /&gt;
Для хранения дерева центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; есть &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; подхода, имеющих свои преимущества и недостатки:&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева запомним величину &amp;lt;math&amp;gt;p_v&amp;lt;/math&amp;gt; {{---}} номер предка вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Этот подход наиболее экономный по памяти &amp;lt;math&amp;gt;(O(n))&amp;lt;/math&amp;gt;, но уступает в скорости и функциональности.&lt;br /&gt;
&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева будем хранить весь массив предков &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt; в дереве центроидов.&lt;br /&gt;
&lt;br /&gt;
Этот подход уступает в количестве необходимой дополнительной памяти &amp;lt;tex&amp;gt;(O(n \cdot \log(n))&amp;lt;/tex&amp;gt; суммарно), но имеет ряд преимуществ:&lt;br /&gt;
&lt;br /&gt;
#При проходе по массиву предков фиксированной вершины будет выигрыш в скорости работы, так как весь массив будет лежать непрерывным блоком данных и следовательно будет закэширован&lt;br /&gt;
# На массиве предков можно строить различные структуры данных (такие как, например, дерево отрезков) для быстрого (в случае с деревом отрезков &amp;lt;math&amp;gt;O(log(\log(n)))&amp;lt;/math&amp;gt; на запрос) поиска предка с необходимыми свойствами. Так, например, в описанной выше задаче про помеченные вершины наибольшего общего предка можно искать методом двоичных подъемов за &amp;lt;math&amp;gt;O(\log(\log(n))&amp;lt;/math&amp;gt; на запрос, так как размер массива предков есть &amp;lt;math&amp;gt;O(\log(n))&amp;lt;/math&amp;gt; (по свойству &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; центроидной декомпозиции). Используя эту оптимизацию можно получить время &amp;lt;math&amp;gt;O(\log(n)) + O(\log(\log(n)) = O(\log(n))&amp;lt;/math&amp;gt; на запрос нахождения ближайшей помеченной вершины. Чтобы добиться улучшенной асимптотики для запросов изменения можно хранить дерево отрезков на каждом из путей &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt;, в каждой вершине которого хранить двоичное дерево поиска и поддерживать отложенные операции. Тогда ответ на эти запросы будет занимать &amp;lt;math&amp;gt;O(\log(n) \cdot \log(\log(n))&amp;lt;/math&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
&lt;br /&gt;
*[[:Дерево_отрезков._Построение|Дерево отрезков]]&lt;br /&gt;
*[[:Heavy-light_декомпозиция|Heavy-light декомпозиция]]&lt;br /&gt;
*[[:Метод_двоичного_подъема| Метод двоичного подъема]]&lt;br /&gt;
&lt;br /&gt;
== Примечания == &lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Источники информации==&lt;br /&gt;
* [https://threads-iiith.quora.com/Centroid-Decomposition-of-a-Tree  Quora {{---}} Centroid Decomposition of a Tree]&lt;br /&gt;
* [http://acm.math.spbu.ru/~sk1/mm/lections/zksh2016-centroid/conspect.pdf  ЗКШ {{---}} Конспект лекции про центроидную декомпозицию]&lt;br /&gt;
* [https://neerc.ifmo.ru/school/archive/2014-2015/ru-olymp-roi-2015-analysis.pdf  Разбор задач РОИ 2015]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы и структуры данных]]&lt;/div&gt;</summary>
		<author><name>92.255.113.52</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61840</id>
		<title>Centroid decomposition</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61840"/>
				<updated>2017-06-25T13:55:34Z</updated>
		
		<summary type="html">&lt;p&gt;92.255.113.52: /* Варианты хранения центроидной декомпозиции */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Центроидная декомпозиция (англ. ''centroid decomposition'') {{---}} это структура данных, позволяющая отвечать на запросы на дереве. Чаще всего это запросы, связанные с нахождением функции на вершинах, связанных неравенством на расстояние между ними в дереве. Также иногда применяется для запросов на путях в дереве.&lt;br /&gt;
&lt;br /&gt;
== Введение ==&lt;br /&gt;
Рассмотрим &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; задачи на обычном массиве (в дальнейшем мы будем их обобщать на случай дерева):&lt;br /&gt;
&lt;br /&gt;
Задача &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть массив &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; положительных целых чисел из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов. Также дано число &amp;lt;tex&amp;gt;W \geqslant 0&amp;lt;/tex&amp;gt; и число &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; индексов массива, таких что &amp;lt;tex&amp;gt;|j - i| \leqslant l &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\sum\limits_{k=i}^{j} a_k \leqslant W&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
Задача &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть прямая дорога, на которой расположены &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; городов. В некоторых городах есть госпитали, которые могут принимать больных. Поступают запросы вида:&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;, в котором находится больной и требуется найти такой город &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, который может принимать больных и &amp;lt;tex&amp;gt;|u - v|&amp;lt;/tex&amp;gt; минимально возможное.&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что больше он не будет принимать больных&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что теперь он может принимать больных&lt;br /&gt;
}}&lt;br /&gt;
Для начала решим обе задачи. &lt;br /&gt;
Первая задача решается методом [[Сортировка_слиянием|«разделяй и властвуй»]] {{---}} давайте разделим массив &amp;lt;tex&amp;gt;a[0 \dots n-1]&amp;lt;/tex&amp;gt; на 2 массива &amp;lt;tex&amp;gt;a[0\dots\dfrac{n}{2} - 1]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;a[\dfrac{n}{2} \dots n-1]&amp;lt;/tex&amp;gt; и рекурсивно решим задачу для каждого из них. Осталось научиться находить количество искомых пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt;, таких что &amp;lt;tex&amp;gt;i &amp;lt; \dfrac{n}{2},\text{ }j \geqslant \dfrac{n}{2}&amp;lt;/tex&amp;gt;. Для этого воспользуемся другой известной техникой {{---}} методом двух указателей. Посчитаем массив префиксных сумм для правой половины &amp;lt;tex&amp;gt;pref[i] = \sum\limits_{j=\frac{n}{2}}^{i} a_j&amp;lt;/tex&amp;gt; и суффиксных &amp;lt;tex&amp;gt;(suf[i] = \sum\limits_{j=i}^{\frac{n}{2} + 1} a_j)&amp;lt;/tex&amp;gt; {{---}} для левой. Заведем два указателя &amp;lt;tex&amp;gt;(p_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;p_2)&amp;lt;/tex&amp;gt;. Изначально установим &amp;lt;tex&amp;gt;p_1 = \dfrac{n}{2} - l + 1,\text{ }p_2 = \dfrac{n}{2}&amp;lt;/tex&amp;gt;. Пока &amp;lt;tex&amp;gt;p_2 - 1&amp;gt; \dfrac{n}{2}&amp;lt;/tex&amp;gt; и&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;pref[p_2] + suf[p_1] &amp;gt; W &amp;lt;/tex&amp;gt; будем уменьшать &amp;lt;math&amp;gt;p_2&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Если после этого &amp;lt;math&amp;gt;pref[p_2] + suf[p_1] \leqslant W&amp;lt;/math&amp;gt;, то к ответу прибавим &amp;lt;math&amp;gt;(p_2 - \dfrac{n}{2} + 1) \cdot (\dfrac{n}{2} - p_1)&amp;lt;/math&amp;gt;, после чего увеличим &amp;lt;math&amp;gt;p_1&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Так будем делать, пока &amp;lt;math&amp;gt;p_1 &amp;lt; \dfrac{n}{2}&amp;lt;/math&amp;gt;. В конце сложим текущий ответ и ответы для половин массива {{---}} получим ответ на задачу. Время работы такого алгоритма: &amp;lt;tex&amp;gt;T(n) = 2 \cdot T(n / 2) + O(n) = O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вторая задача имеет запросы на изменение и поэтому надо применить динамическую версию «разделяй и властвуй» {{---}} [[Дерево_отрезков._Построение|дерево отрезков]]. Построим дерево отрезков,&lt;br /&gt;
поддерживающее 2 вида запросов: присвоение в точке и минимум на отрезке. Изначально сделаем так, чтобы дереву отрезков соответствовал массив &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;b_i = 0&amp;lt;/tex&amp;gt;, если в &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м городе принимает госпиталь и &amp;lt;tex&amp;gt;b_i = 1&amp;lt;/tex&amp;gt; иначе. Когда в каком-то городе открывается/закрывается госпиталь {{---}} делаем запрос на изменение в дереве отрезков. Когда требуется узнать ближайший госпиталь к &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-му городу, можно воспользоваться одной из следующих идей: &lt;br /&gt;
* &amp;lt;tex&amp;gt;(O(n \cdot log^2(n))&amp;lt;/tex&amp;gt; Бинарным поиском ищем ближайший слева и ближайший справа к &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-му городу госпиталь (такой город &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, что &amp;lt;tex&amp;gt;\min\limits_{k=i..j}b_k= 1)&amp;lt;/tex&amp;gt;. Для этого внутри бинарного поиска каждый раз делаем запрос на поиск минимума в дереве отрезков.  &lt;br /&gt;
* &amp;lt;tex&amp;gt;(O(n \cdot \log(n))&amp;lt;/tex&amp;gt; Будем одним спуском/подъемом по дереву определять, куда нам нужно идти (в левое или правое поддерево), тем самым делая одновременно и бинарный поиск, и спуск/подъем по дереву.&lt;br /&gt;
&lt;br /&gt;
== Статическая центроидная декомпозиция ==&lt;br /&gt;
Перейдем к обобщению поставленных задач на случай дерева.&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть взвешенное дерево &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин, в каждой вершине которого написаны положительные целые числа. Также по-прежнему даны числа &amp;lt;tex&amp;gt;W \geqslant 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; вершин дерева, таких что расстояние между ними не превосходит &amp;lt;math&amp;gt;l&amp;lt;/math&amp;gt; по числу ребер и не превосходит &amp;lt;math&amp;gt;W&amp;lt;/math&amp;gt; по сумме весов&amp;lt;ref&amp;gt;[http://neerc.ifmo.ru/school/camp-2016/problems/20160324a1.pdf  Сайт с задачами Санкт-Петербургских сборов к РОИ 2016]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Для решения новой задачи применим ту же идею, что и была до этого {{---}} «разделяй и властвуй». Для этого нам потребуется следующий объект:&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|id=191213&lt;br /&gt;
|definition =&lt;br /&gt;
'''Центроидом дерева''' (англ. ''centroid'') называется такая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, после удаления которой дерево разбивается на несколько &amp;lt;math&amp;gt;(k)&amp;lt;/math&amp;gt; поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, таких что для каждого &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;: &amp;lt;tex&amp;gt;|t_i| \leqslant \dfrac{n}{2}&amp;lt;/tex&amp;gt;, то есть размер каждого поддерева не превосходит половины размера исходного дерева.&lt;br /&gt;
}}&lt;br /&gt;
Итак, в случае дерева идея «разделяй и властвуй» из предыдущего пункта будет формулироваться так: [[#l1|найдем центроид]]. Предположим, что мы сумели найти центроид за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; {{---}} размер дерева. Тогда, как и в упрощенной версии задачи {{---}} рекурсивно найдем ответ для всех поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, после чего попытаемся найти недостающие пары вершин, находящиеся в разных поддеревьях и удовлетворяющих вопросу задачи. Для этого будем отвечать на следующие запросы: пусть мы сейчас считаем все пары, где первая из вершин находится в поддереве &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и мы в некоторой структуре данных &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; храним все вершины остальных деревьев (каждую вершину задаем парой &amp;lt;math&amp;gt;(depth(v), length(v))&amp;lt;/math&amp;gt; {{---}} глубина вершины и длина пути до нее из корня поддерева), расстояние до которых от корня их поддерева не превышает &amp;lt;math&amp;gt;min(l, n)&amp;lt;/math&amp;gt;. Тогда просто пройдемся по всем вершинам &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; поддерева &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и прибавим к ответу число вершин в структуре &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt;, таких, что &amp;lt;tex&amp;gt;depth(u) \leqslant l - depth(v)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;length(u) \leqslant l - length(v)&amp;lt;/tex&amp;gt;. Это двумерные запросы, на которые можно отвечать за &amp;lt;math&amp;gt;O(log^2(n)&amp;lt;/math&amp;gt; с помощью [[Многомерное_дерево_отрезков|2d-дерева отрезков]], либо за &amp;lt;math&amp;gt;O(\log(n))&amp;lt;/math&amp;gt; с помощью [[Перечисление_точек_в_произвольном_прямоугольнике_за_n_*_log_%5E(d_-_1)_n_(range_tree)|техники поиска точек в d-мерном пространстве]]. Также читателю предлагается придумать и более эффективные и простые способы решить эту подзадачу.&lt;br /&gt;
&lt;br /&gt;
Оценим итоговое время работы алгоритма: &amp;lt;tex&amp;gt;T(n) = k \cdot T(n / k) + O(n \cdot \log(n))&amp;lt;/tex&amp;gt;. Решая это рекуррентное соотношение, получим &amp;lt;math&amp;gt;O(n \cdot \log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь докажем лемму о существовании центроида и опишем алгоритм его эффективного поиска.&lt;br /&gt;
&lt;br /&gt;
=== Лемма о существовании центроида и алгоритм его нахождения. ===&lt;br /&gt;
{{Лемма|id=l1&lt;br /&gt;
|statement=&lt;br /&gt;
В любом дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; существует центроид.&lt;br /&gt;
|proof=&lt;br /&gt;
Рассмотрим корень дерева &amp;lt;math&amp;gt;(r)&amp;lt;/math&amp;gt;. Положим изначально &amp;lt;math&amp;gt;v = r&amp;lt;/math&amp;gt;. Изначально &amp;lt;math&amp;gt;|subtree(v)| = n&amp;lt;/math&amp;gt;. Среди всех детей &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; выберем вершину &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; с максимальным размером поддерева. Если &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид, то положим &amp;lt;math&amp;gt;v = u&amp;lt;/math&amp;gt; и продолжим выбор нового &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, иначе {{---}} остановимся. Докажем, что мы в какой-то момент остановимся. Пусть в произвольный момент времени &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид и размер её наддерева меньше &amp;lt;math&amp;gt;\dfrac{n}{2}&amp;lt;/math&amp;gt;, значит максимальное поддерево имеет размер больше чем &amp;lt;math&amp;gt;\dfrac{n}{2}&amp;lt;/math&amp;gt;, то есть &amp;lt;math&amp;gt;|subtree(u)| &amp;gt; \dfrac{n}{2}&amp;lt;/math&amp;gt;, а значит размер &amp;quot;наддерева&amp;quot; вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; равен &amp;lt;tex&amp;gt;n - |subtree(u)| &amp;lt; \dfrac{n}{2}&amp;lt;/tex&amp;gt;. При этом теперь размер любого поддерева, на которое распадется дерево t при удалении вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; не превосходит &amp;lt;math&amp;gt;|subtree(u)| - 1&amp;lt;/math&amp;gt;, так как наддерево имеет размер меньше, чем поддерево &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, а любое поддерево вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; имеет хотя бы на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; вершину меньше (сама вершина &amp;lt;math&amp;gt;u)&amp;lt;/math&amp;gt;. По индукции получаем, что в любой момент времени размер наддерева вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; меньше &amp;lt;math&amp;gt;\dfrac{n}{2}&amp;lt;/math&amp;gt;, значит мы будем спускаться только вниз по дереву &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, и при переходе к вершине &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} сыну &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; размер максимального поддерева уменьшится как минимум на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Значит не более чем за &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; шагов наши действия прекратятся и мы окажемся в центроиде дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;.&lt;br /&gt;
Итак, мы конструктивно доказали существование центроида и привели линейный относительно размера дерева алгоритм его нахождения. &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Реализация ===&lt;br /&gt;
&lt;br /&gt;
Поиск центроида в дереве:&lt;br /&gt;
&lt;br /&gt;
  '''int''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int[]''' sz)&lt;br /&gt;
     max_subtree = -1&lt;br /&gt;
     '''for''' u : children(v)&lt;br /&gt;
          '''if''' sz[u] &amp;gt; sz[v] / 2 '''and''' sz[u] &amp;gt; sz[max_subtree] &amp;lt;font color=green&amp;gt;// считаем sz[-1] = 0 &amp;lt;/font &amp;gt;&lt;br /&gt;
               max_subtree = u&lt;br /&gt;
     '''if''' max_subtree == -1&lt;br /&gt;
          return v&lt;br /&gt;
     '''else''' &lt;br /&gt;
          '''return''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz)&lt;br /&gt;
&lt;br /&gt;
Шаблон решения произвольной задачи на статическую центроидную декомпозицию:&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int[]''' sz)&lt;br /&gt;
     c = &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz) &amp;lt;font color=green&amp;gt;// находим c {{---}} центроид поддерева вершины v &amp;lt;/font &amp;gt;&lt;br /&gt;
     ch = children[c]&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{deleteEdges}&amp;lt;/tex&amp;gt;(c, children) &amp;lt;font color=green&amp;gt;// удаляем все ребра между вершиной с и детьми, чтобы мы не смогли из детей попасть в с. &lt;br /&gt;
         Это полезно делать, если решение подзадачи для поддерева предполагает проход &amp;lt;math&amp;gt;dfs&amp;lt;/math&amp;gt; &amp;lt;/font &amp;gt;&lt;br /&gt;
     '''for''' c2 : ch[c]&lt;br /&gt;
          &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;(children, c2, sz)&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{mergeSolution}&amp;lt;/tex&amp;gt;(children, c2) &amp;lt;font color=green&amp;gt;// решаем для текущего поддерева &amp;lt;/font &amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Динамическая центроидная декомпозиция (дерево центроидной декомпозиции) ==&lt;br /&gt;
Теперь вернемся ко второй задаче введения. Для ее решения мы пользовались динамичекой версией «разделяй и властвуй» {{---}} деревом отрезков. В предыдущем пункте мы определили статический аналог «разделяй и властвуй» для дерева. Теперь обобщим этот метод для динамических задач.&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Деревом центроидов''' (или центроидной декомпозицией, англ. ''centroid decomposition tree'') дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; называют дерево &amp;lt;math&amp;gt;T(t)&amp;lt;/math&amp;gt;, построенное на вершинах дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; следующим образом:&lt;br /&gt;
* Пусть вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} центроид дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Объявим его корнем нового дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пусть при удалении вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; из дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; оно распалось на поддеревья &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, тогда детьми вершины c объявим &amp;lt;tex&amp;gt;T(t_1), T(t_t),\dots, T(t_k)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
=== Свойства центроидной декомпозиции ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
'''Свойства центроидной декомпозиции''':&lt;br /&gt;
# Глубина дерева центроидов не превосходит &amp;lt;tex&amp;gt;\\log(n)&amp;lt;/tex&amp;gt;. &lt;br /&gt;
# Каждая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; является центроидом одного из поддеревьев дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждая вершина дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; принадлежит &amp;lt;math&amp;gt;O(\log(n))&amp;lt;/math&amp;gt; поддеревьям дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (еще говорят, что вершина принадлежит &amp;lt;math&amp;gt;O(\log(n))&amp;lt;/math&amp;gt; центроидам дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, или что эти центроиды содержат вершину)&lt;br /&gt;
# Для любых вершин &amp;lt;tex&amp;gt;u, v \in T (u \neq v)&amp;lt;/tex&amp;gt; верно ровно одно из следующих трех утверждений: &lt;br /&gt;
#*&amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;&lt;br /&gt;
#* &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;&lt;br /&gt;
#* &amp;lt;tex&amp;gt;T(u) \cap T(v) = \emptyset &amp;lt;/tex&amp;gt;&lt;br /&gt;
# Простой путь между любой парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; содержит центроид &amp;lt;tex&amp;gt;c \in T(t)&amp;lt;/tex&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
# Действительно, так как размер поддерева &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; не превосходит &amp;lt;tex&amp;gt;\dfrac{|subtree(c)|}{2}&amp;lt;/tex&amp;gt;, то при спуске в каждую следующую вершину на пути к любому листу в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; размер поддерева вершины, в которой мы сейчас находимся, уменьшается как минимум на &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;. Значит длина всего пути до листа не превосходит &amp;lt;math&amp;gt;\log(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Второе свойство очевидно из построения дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, так как если вершина принадлежит дереву центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то она является центроидом, а из построения &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы знаем, что каждая вершина исходного дерева принадлежит и дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Третье свойство {{---}} прямое следствие первых двух, так как вершина принадлежит любому центроиду &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; т.и т.т., когда &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} отец вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов. Так как вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; точно принадлежит дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (свойство &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;), то она лежит на каком-то пути в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, причем все ее родители (центроиды) ее содержат. А по свойству &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; длина любого вертикального (и даже простого) пути есть &amp;lt;math&amp;gt;O(\log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Четвертое свойство очевидно из того, что &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; {{---}} дерево. Так как &amp;lt;math&amp;gt;T(u)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;T(v)&amp;lt;/math&amp;gt; {{---}} поддеревья различных вершин дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то либо они не пересекаются, либо &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;, либо &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Для доказательства последнего свойства в качестве вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; выберем &amp;lt;math&amp;gt;lca(u, v)&amp;lt;/math&amp;gt; в дереве центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Покажем, что так выбранная вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удовлетворяет заявленным свойствам. То, что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt; {{---}} очевидно по определению &amp;lt;math&amp;gt;lca&amp;lt;/math&amp;gt;, так как каждый предок любой вершины в дереве центроидов содержит эту вершину. Теперь докажем, что &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежит на пути между парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt;. Так как &amp;lt;math&amp;gt;c = lca(u, v)&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то из &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; нет ребра в такого сына, который содержит одновременно &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в своем поддереве (в дереве &amp;lt;math&amp;gt;T)&amp;lt;/math&amp;gt;, значит после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; разделится на несколько поддеревьев, таких что вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; окажутся в разных компонентах связности. А значит найдется такое ребро &amp;lt;math&amp;gt;(c, x)&amp;lt;/math&amp;gt;, которое принадлежало пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, но после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удалилось. Это доказывает то, что вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежала на пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
С помощью описанных свойств дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы можем решить задачу &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; для дерева:&lt;br /&gt;
&lt;br /&gt;
=== Пример решения задачи с помощью центроидной декомпозиции === &lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин. В каждый момент времени любая вершина дерева может быть либо помечена, либо нет. Изначально помечена только вершина номер &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Дан список из &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; запросов: &lt;br /&gt;
* Вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; пометили.&lt;br /&gt;
* С вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; сняли пометку.&lt;br /&gt;
* Найти номер ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины&amp;lt;ref&amp;gt;[http://neerc.ifmo.ru/school/camp-2016/problems/20160324a1.pdf  Сайт с задачами Санкт-Петербургских сборов к РОИ 2016]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Решение ====&lt;br /&gt;
&lt;br /&gt;
Построим центроидную декомпозицию &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Изначально посчитаем для каждого центроида, содержащего вершину &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt; посчитаем расстояние до вершины &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Для этого воспользуемся [[Метод_двоичного_подъема|методом двоичных подъемов для поиска lca пары вершин в дереве]], а также тем фактом, что если глубина &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве определена как расстояние от корня до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то длина пути между парой вершин &amp;lt;math&amp;gt;(u, v)&amp;lt;/math&amp;gt; есть &amp;lt;tex&amp;gt;len(u, v) = h(u) + h(v) - 2 \cdot h(lca(u, v))&amp;lt;/tex&amp;gt;. Если изначально предпосчитать проходом [[Обход_в_глубину,_цвета_вершин| &amp;lt;math&amp;gt;dfs&amp;lt;/math&amp;gt;]] величины &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, то ответ на запрос &amp;lt;math&amp;gt;len(u, v)&amp;lt;/math&amp;gt; можно делать за время &amp;lt;math&amp;gt;O(\log(n))&amp;lt;/math&amp;gt; с &amp;lt;tex&amp;gt;O(n \cdot \log(n)) &amp;lt;/tex&amp;gt;доп. памятью. Также можно воспользоваться техникой [[Сведение_задачи_LCA_к_задаче_RMQ|сведения задачи LCA к RMQ]] и решить с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; времени на запрос. Теперь научимся отвечать на запросы. Из последнего свойства центроидной декомпозиции видно, что если &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} искомая ближайшая помеченная вершина к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то путь между ними содержит центроид &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;, причем &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок одновременно вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве&amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Поэтому заведем [[Красно-черное_дерево|двоичное дерево поиска]] для каждого центроида &amp;lt;math&amp;gt;c \in T&amp;lt;/math&amp;gt;. В этой структуре для каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; будем хранить пары &amp;lt;math&amp;gt;(len(c, u), u)&amp;lt;/math&amp;gt; для всех помеченных вершин &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в поддереве центроидов &amp;lt;math&amp;gt;T(c)&amp;lt;/math&amp;gt;. Когда приходит запрос пометить вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} добавим в структуру данных для всех предков &amp;lt;math&amp;gt;p_c&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; пары &amp;lt;math&amp;gt;(len(p_c, v), v)&amp;lt;/math&amp;gt;. Мы совершим &amp;lt;math&amp;gt;O(\log(n))&amp;lt;/math&amp;gt; добавлений, затратив &amp;lt;math&amp;gt;O(\log(n))&amp;lt;/math&amp;gt; действий на каждое. Запрос снятия пометки с вершины обрабатывается аналогичными удалениями. Запрос поиска ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины {{---}} это запрос поиска вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, такой что  величина &amp;lt;math&amp;gt;len(c, u) + len(c, v)&amp;lt;/math&amp;gt; минимальна, где &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов (по пятому свойству, нас интересуют именно такие &amp;lt;math&amp;gt;c)&amp;lt;/math&amp;gt;. Этот запрос занимает так же &amp;lt;tex&amp;gt;O(log^2(n))&amp;lt;/tex&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
Итак, мы научились решать задачу с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и временной сложностью &amp;lt;math&amp;gt;O(log^2(n))&amp;lt;/math&amp;gt; на запрос любого типа с предварительным предпосчетом за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Варианты хранения центроидной декомпозиции ==&lt;br /&gt;
Для хранения дерева центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; есть &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; подхода, имеющих свои преимущества и недостатки:&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева запомним величину &amp;lt;math&amp;gt;p_v&amp;lt;/math&amp;gt; {{---}} номер предка вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Этот подход наиболее экономный по памяти &amp;lt;math&amp;gt;(O(n))&amp;lt;/math&amp;gt;, но уступает в скорости и функциональности.&lt;br /&gt;
&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева будем хранить весь массив предков &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt; в дереве центроидов.&lt;br /&gt;
&lt;br /&gt;
Этот подход уступает в количестве необходимой дополнительной памяти &amp;lt;tex&amp;gt;(O(n \cdot \log(n))&amp;lt;/tex&amp;gt; суммарно), но имеет ряд преимуществ:&lt;br /&gt;
&lt;br /&gt;
#При проходе по массиву предков фиксированной вершины будет выигрыш в скорости работы, так как весь массив будет лежать непрерывным блоком данных и следовательно будет закэширован&lt;br /&gt;
# На массиве предков можно строить различные структуры данных (такие как, например, дерево отрезков) для быстрого (в случае с деревом отрезков &amp;lt;math&amp;gt;O(log(\log(n)))&amp;lt;/math&amp;gt; на запрос) поиска предка с необходимыми свойствами. Так, например, в описанной выше задаче про помеченные вершины наибольшего общего предка можно искать методом двоичных подъемов за &amp;lt;math&amp;gt;O(\log(\log(n))&amp;lt;/math&amp;gt; на запрос, так как размер массива предков есть &amp;lt;math&amp;gt;O(\log(n))&amp;lt;/math&amp;gt; (по свойству &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; центроидной декомпозиции). Используя эту оптимизацию можно получить время &amp;lt;math&amp;gt;O(\log(n)) + O(log(\log(n)) = O(\log(n))&amp;lt;/math&amp;gt; на запрос нахождения ближайшей помеченной вершины. Чтобы добиться улучшенной асимптотики для запросов изменения можно хранить дерево отрезков на каждом из путей &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt;, в каждой вершине которого хранить двоичное дерево поиска и поддерживать отложенные операции. Тогда ответ на эти запросы будет занимать &amp;lt;math&amp;gt;O(\log(n) \cdot \log(\log(n))&amp;lt;/math&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
&lt;br /&gt;
*[[:Дерево_отрезков._Построение|Дерево отрезков]]&lt;br /&gt;
*[[:Heavy-light_декомпозиция|Heavy-light декомпозиция]]&lt;br /&gt;
*[[:Метод_двоичного_подъема| Метод двоичного подъема]]&lt;br /&gt;
&lt;br /&gt;
== Примечания == &lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Источники информации==&lt;br /&gt;
* [https://threads-iiith.quora.com/Centroid-Decomposition-of-a-Tree  Quora {{---}} Centroid Decomposition of a Tree]&lt;br /&gt;
* [http://acm.math.spbu.ru/~sk1/mm/lections/zksh2016-centroid/conspect.pdf  ЗКШ {{---}} Конспект лекции про центроидную декомпозицию]&lt;br /&gt;
* [https://neerc.ifmo.ru/school/archive/2014-2015/ru-olymp-roi-2015-analysis.pdf  Разбор задач РОИ 2015]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы и структуры данных]]&lt;/div&gt;</summary>
		<author><name>92.255.113.52</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61760</id>
		<title>Centroid decomposition</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61760"/>
				<updated>2017-06-22T17:42:18Z</updated>
		
		<summary type="html">&lt;p&gt;92.255.113.52: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Центроидная декомпозиция (англ. ''centroid decomposition'') {{---}} это структура данных, позволяющая отвечать на запросы на дереве. Чаще всего это запросы, связанные с нахождением функции на вершинах, связанных неравенством на расстояние между ними в дереве. Также иногда применяется для запросов на путях в дереве.&lt;br /&gt;
&lt;br /&gt;
== Введение ==&lt;br /&gt;
Рассмотрим &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; задачи на обычном массиве (в дальнейшем мы будем их обобщать на случай дерева):&lt;br /&gt;
&lt;br /&gt;
Задача &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть массив &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; положительных целых чисел из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов. Также дано число &amp;lt;tex&amp;gt;W \geqslant 0&amp;lt;/tex&amp;gt; и число &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; индексов массива, таких что &amp;lt;tex&amp;gt;|j - i| \leqslant l &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\sum\limits_{k=i}^{j} a_k \leqslant W&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
Задача &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть прямая дорога, на которой расположены &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; городов. В некоторых городах есть госпитали, которые могут принимать больных. Поступают запросы вида:&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;, в котором находится больной и требуется найти такой город &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, который может принимать больных и &amp;lt;tex&amp;gt;|u - v|&amp;lt;/tex&amp;gt; минимально возможное.&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что больше он не будет принимать больных&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что теперь он может принимать больных&lt;br /&gt;
}}&lt;br /&gt;
Для начала решим обе задачи. &lt;br /&gt;
Первая задача решается методом [[Сортировка_слиянием|«разделяй и властвуй»]] {{---}} давайте разделим массив &amp;lt;tex&amp;gt;a[0 \dots n-1]&amp;lt;/tex&amp;gt; на 2 массива &amp;lt;tex&amp;gt;a[0\dots\dfrac{n}{2} - 1]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;a[\dfrac{n}{2} \dots n-1]&amp;lt;/tex&amp;gt; и рекурсивно решим задачу для каждого из них. Осталось научиться находить количество искомых пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt;, таких что &amp;lt;tex&amp;gt;i &amp;lt; \dfrac{n}{2},\text{ }j \geqslant \dfrac{n}{2}&amp;lt;/tex&amp;gt;. Для этого воспользуемся другой известной техникой {{---}} методом двух указателей. Посчитаем массив префиксных сумм для правой половины &amp;lt;tex&amp;gt;pref[i] = \sum\limits_{j=\frac{n}{2}}^{i} a_j&amp;lt;/tex&amp;gt; и суффиксных &amp;lt;tex&amp;gt;(suf[i] = \sum\limits_{j=i}^{\frac{n}{2} + 1} a_j)&amp;lt;/tex&amp;gt; {{---}} для левой. Заведем два указателя &amp;lt;tex&amp;gt;(p_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;p_2)&amp;lt;/tex&amp;gt;. Изначально установим &amp;lt;tex&amp;gt;p_1 = \dfrac{n}{2} - l + 1,\text{ }p_2 = \dfrac{n}{2}&amp;lt;/tex&amp;gt;. Пока &amp;lt;tex&amp;gt;p_2 - 1&amp;gt; \dfrac{n}{2}&amp;lt;/tex&amp;gt; и&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;pref[p_2] + suf[p_1] &amp;gt; W &amp;lt;/tex&amp;gt; будем уменьшать &amp;lt;math&amp;gt;p_2&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Если после этого &amp;lt;math&amp;gt;pref[p_2] + suf[p_1] \leqslant W&amp;lt;/math&amp;gt;, то к ответу прибавим &amp;lt;math&amp;gt;(p_2 - \dfrac{n}{2} + 1) \cdot (\dfrac{n}{2} - p_1)&amp;lt;/math&amp;gt;, после чего увеличим &amp;lt;math&amp;gt;p_1&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Так будем делать, пока &amp;lt;math&amp;gt;p_1 &amp;lt; \dfrac{n}{2}&amp;lt;/math&amp;gt;. В конце сложим текущий ответ и ответы для половин массива {{---}} получим ответ на задачу. Время работы такого алгоритма: &amp;lt;tex&amp;gt;T(n) = 2 \cdot T(n / 2) + O(n) = O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вторая задача имеет запросы на изменение и поэтому надо применить динамическую версию «разделяй и властвуй» {{---}} [[Дерево_отрезков._Построение|дерево отрезков]]. Построим дерево отрезков,&lt;br /&gt;
поддерживающее 2 вида запросов: присвоение в точке и минимум на отрезке. Изначально сделаем так, чтобы дереву отрезков соответствовал массив &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;b_i = 0&amp;lt;/tex&amp;gt;, если в &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м городе принимает госпиталь и &amp;lt;tex&amp;gt;b_i = 1&amp;lt;/tex&amp;gt; иначе. Когда в каком-то городе открывается/закрывается госпиталь {{---}} делаем запрос на изменение в дереве отрезков. Когда требуется узнать ближайший госпиталь к &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-му городу, можно воспользоваться одной из следующих идей: &lt;br /&gt;
* &amp;lt;tex&amp;gt;(O(n \cdot log^2(n))&amp;lt;/tex&amp;gt; Бинарным поиском ищем ближайший слева и ближайший справа к &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-му городу госпиталь (такой город &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, что &amp;lt;tex&amp;gt;\min\limits_{k=i..j}b_k= 1)&amp;lt;/tex&amp;gt;. Для этого внутри бинарного поиска каждый раз делаем запрос на поиск минимума в дереве отрезков.  &lt;br /&gt;
* &amp;lt;tex&amp;gt;(O(n \cdot \log(n))&amp;lt;/tex&amp;gt; Будем одним спуском/подъемом по дереву определять, куда нам нужно идти (в левое или правое поддерево), тем самым делая одновременно и бинарный поиск, и спуск/подъем по дереву.&lt;br /&gt;
&lt;br /&gt;
== Статическая центроидная декомпозиция ==&lt;br /&gt;
Перейдем к обобщению поставленных задач на случай дерева.&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть взвешенное дерево &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин, в каждой вершине которого написаны положительные целые числа. Также по-прежнему даны числа &amp;lt;tex&amp;gt;W \geqslant 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; вершин дерева, таких что расстояние между ними не превосходит &amp;lt;math&amp;gt;l&amp;lt;/math&amp;gt; по числу ребер и не превосходит &amp;lt;math&amp;gt;W&amp;lt;/math&amp;gt; по сумме весов&amp;lt;ref&amp;gt;[http://neerc.ifmo.ru/school/camp-2016/problems/20160324a1.pdf  Сайт с задачами Санкт-Петербургских сборов к РОИ 2016]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Для решения новой задачи применим ту же идею, что и была до этого {{---}} «разделяй и властвуй». Для этого нам потребуется следующий объект:&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|id=191213&lt;br /&gt;
|definition =&lt;br /&gt;
'''Центроидом дерева''' (англ. ''centroid'') называется такая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, после удаления которой дерево разбивается на несколько &amp;lt;math&amp;gt;(k)&amp;lt;/math&amp;gt; поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, таких что для каждого &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;: &amp;lt;tex&amp;gt;|t_i| \leqslant \dfrac{n}{2}&amp;lt;/tex&amp;gt;, то есть размер каждого поддерева не превосходит половины размера исходного дерева.&lt;br /&gt;
}}&lt;br /&gt;
Итак, в случае дерева идея «разделяй и властвуй» из предыдущего пункта будет формулироваться так: [[#l1|найдем центроид]]. Предположим, что мы сумели найти центроид за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; {{---}} размер дерева. Тогда, как и в упрощенной версии задачи {{---}} рекурсивно найдем ответ для всех поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, после чего попытаемся найти недостающие пары вершин, находящиеся в разных поддеревьях и удовлетворяющих вопросу задачи. Для этого будем отвечать на следующие запросы: пусть мы сейчас считаем все пары, где первая из вершин находится в поддереве &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и мы в некоторой структуре данных &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; храним все вершины остальных деревьев (каждую вершину задаем парой &amp;lt;math&amp;gt;(depth(v), length(v))&amp;lt;/math&amp;gt; {{---}} глубина вершины и длина пути до нее из корня поддерева), расстояние до которых от корня их поддерева не превышает &amp;lt;math&amp;gt;min(l, n)&amp;lt;/math&amp;gt;. Тогда просто пройдемся по всем вершинам &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; поддерева &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и прибавим к ответу число вершин в структуре &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt;, таких, что &amp;lt;tex&amp;gt;depth(u) \leqslant l - depth(v)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;length(u) \leqslant l - length(v)&amp;lt;/tex&amp;gt;. Это двумерные запросы, на которые можно отвечать за &amp;lt;math&amp;gt;O(log^2(n)&amp;lt;/math&amp;gt; с помощью [[Многомерное_дерево_отрезков|2d-дерева отрезков]], либо за &amp;lt;math&amp;gt;O(\log(n))&amp;lt;/math&amp;gt; с помощью [[Перечисление_точек_в_произвольном_прямоугольнике_за_n_*_log_%5E(d_-_1)_n_(range_tree)|техники поиска точек в d-мерном пространстве]]. Также читателю предлагается придумать и более эффективные и простые способы решить эту подзадачу.&lt;br /&gt;
&lt;br /&gt;
Оценим итоговое время работы алгоритма: &amp;lt;tex&amp;gt;T(n) = k \cdot T(n / k) + O(n \cdot \log(n))&amp;lt;/tex&amp;gt;. Решая это рекуррентное соотношение, получим &amp;lt;math&amp;gt;O(n \cdot \log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь докажем лемму о существовании центроида и опишем алгоритм его эффективного поиска.&lt;br /&gt;
&lt;br /&gt;
=== Лемма о существовании центроида и алгоритм его нахождения. ===&lt;br /&gt;
{{Лемма|id=l1&lt;br /&gt;
|statement=&lt;br /&gt;
В любом дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; существует центроид.&lt;br /&gt;
|proof=&lt;br /&gt;
Рассмотрим корень дерева &amp;lt;math&amp;gt;(r)&amp;lt;/math&amp;gt;. Положим изначально &amp;lt;math&amp;gt;v = r&amp;lt;/math&amp;gt;. Изначально &amp;lt;math&amp;gt;|subtree(v)| = n&amp;lt;/math&amp;gt;. Среди всех детей &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; выберем вершину &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; с максимальным размером поддерева. Если &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид, то положим &amp;lt;math&amp;gt;v = u&amp;lt;/math&amp;gt; и продолжим выбор нового &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, иначе {{---}} остановимся. Докажем, что мы в какой-то момент остановимся. Пусть в произвольный момент времени &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид и размер её наддерева меньше &amp;lt;math&amp;gt;\dfrac{n}{2}&amp;lt;/math&amp;gt;, значит максимальное поддерево имеет размер больше чем &amp;lt;math&amp;gt;\dfrac{n}{2}&amp;lt;/math&amp;gt;, то есть &amp;lt;math&amp;gt;|subtree(u)| &amp;gt; \dfrac{n}{2}&amp;lt;/math&amp;gt;, а значит размер &amp;quot;наддерева&amp;quot; вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; равен &amp;lt;tex&amp;gt;n - |subtree(u)| &amp;lt; \dfrac{n}{2}&amp;lt;/tex&amp;gt;. При этом теперь размер любого поддерева, на которое распадется дерево t при удалении вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; не превосходит &amp;lt;math&amp;gt;|subtree(u)| - 1&amp;lt;/math&amp;gt;, так как наддерево имеет размер меньше, чем поддерево &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, а любое поддерево вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; имеет хотя бы на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; вершину меньше (сама вершина &amp;lt;math&amp;gt;u)&amp;lt;/math&amp;gt;. По индукции получаем, что в любой момент времени размер наддерева вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; меньше &amp;lt;math&amp;gt;\dfrac{n}{2}&amp;lt;/math&amp;gt;, значит мы будем спускаться только вниз по дереву &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, и при переходе к вершине &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} сыну &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; размер максимального поддерева уменьшится как минимум на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Значит не более чем за &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; шагов наши действия прекратятся и мы окажемся в центроиде дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;.&lt;br /&gt;
Итак, мы конструктивно доказали существование центроида и привели линейный относительно размера дерева алгоритм его нахождения. &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Реализация ===&lt;br /&gt;
&lt;br /&gt;
Поиск центроида в дереве:&lt;br /&gt;
&lt;br /&gt;
  '''int''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int[]''' sz)&lt;br /&gt;
     max_subtree = -1&lt;br /&gt;
     '''for''' u : children(v)&lt;br /&gt;
          '''if''' sz[u] &amp;gt; sz[v] / 2 '''and''' sz[u] &amp;gt; sz[max_subtree] &amp;lt;font color=green&amp;gt;// считаем sz[-1] = 0 &amp;lt;/font &amp;gt;&lt;br /&gt;
               max_subtree = u&lt;br /&gt;
     '''if''' max_subtree == -1&lt;br /&gt;
          return v&lt;br /&gt;
     '''else''' &lt;br /&gt;
          '''return''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz)&lt;br /&gt;
&lt;br /&gt;
Шаблон решения произвольной задачи на статическую центроидную декомпозицию:&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int[]''' sz)&lt;br /&gt;
     c = &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz) &amp;lt;font color=green&amp;gt;// находим c {{---}} центроид поддерева вершины v &amp;lt;/font &amp;gt;&lt;br /&gt;
     ch = children[c]&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{deleteEdges}&amp;lt;/tex&amp;gt;(c, children) &amp;lt;font color=green&amp;gt;// удаляем все ребра между вершиной с и детьми, чтобы мы не смогли из детей попасть в с. &lt;br /&gt;
         Это полезно делать, если решение подзадачи для поддерева предполагает проход &amp;lt;math&amp;gt;dfs&amp;lt;/math&amp;gt; &amp;lt;/font &amp;gt;&lt;br /&gt;
     '''for''' c2 : ch[c]&lt;br /&gt;
          &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;(children, c2, sz)&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{mergeSolution}&amp;lt;/tex&amp;gt;(children, c2) &amp;lt;font color=green&amp;gt;// решаем для текущего поддерева &amp;lt;/font &amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Динамическая центроидная декомпозиция (дерево центроидной декомпозиции) ==&lt;br /&gt;
Теперь вернемся ко второй задаче введения. Для ее решения мы пользовались динамичекой версией «разделяй и властвуй» {{---}} деревом отрезков. В предыдущем пункте мы определили статический аналог «разделяй и властвуй» для дерева. Теперь обобщим этот метод для динамических задач.&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Деревом центроидов''' (или центроидной декомпозицией, англ. ''centroid decomposition tree'') дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; называют дерево &amp;lt;math&amp;gt;T(t)&amp;lt;/math&amp;gt;, построенное на вершинах дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; следующим образом:&lt;br /&gt;
* Пусть вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} центроид дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Объявим его корнем нового дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пусть при удалении вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; из дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; оно распалось на поддеревья &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, тогда детьми вершины c объявим &amp;lt;tex&amp;gt;T(t_1), T(t_t),\dots, T(t_k)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
=== Свойства центроидной декомпозиции ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
'''Свойства центроидной декомпозиции''':&lt;br /&gt;
# Глубина дерева центроидов не превосходит &amp;lt;tex&amp;gt;\\log(n)&amp;lt;/tex&amp;gt;. &lt;br /&gt;
# Каждая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; является центроидом одного из поддеревьев дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждая вершина дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; принадлежит &amp;lt;math&amp;gt;O(\log(n))&amp;lt;/math&amp;gt; поддеревьям дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (еще говорят, что вершина принадлежит &amp;lt;math&amp;gt;O(\log(n))&amp;lt;/math&amp;gt; центроидам дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, или что эти центроиды содержат вершину)&lt;br /&gt;
# Для любых вершин &amp;lt;tex&amp;gt;u, v \in T (u \neq v)&amp;lt;/tex&amp;gt; верно ровно одно из следующих трех утверждений: &lt;br /&gt;
#*&amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;&lt;br /&gt;
#* &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;&lt;br /&gt;
#* &amp;lt;tex&amp;gt;T(u) \cap T(v) = \emptyset &amp;lt;/tex&amp;gt;&lt;br /&gt;
# Простой путь между любой парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; содержит центроид &amp;lt;tex&amp;gt;c \in T(t)&amp;lt;/tex&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
# Действительно, так как размер поддерева &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; не превосходит &amp;lt;tex&amp;gt;\dfrac{|subtree(c)|}{2}&amp;lt;/tex&amp;gt;, то при спуске в каждую следующую вершину на пути к любому листу в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; размер поддерева вершины, в которой мы сейчас находимся, уменьшается как минимум на &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;. Значит длина всего пути до листа не превосходит &amp;lt;math&amp;gt;\log(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Второе свойство очевидно из построения дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, так как если вершина принадлежит дереву центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то она является центроидом, а из построения &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы знаем, что каждая вершина исходного дерева принадлежит и дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Третье свойство {{---}} прямое следствие первых двух, так как вершина принадлежит любому центроиду &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; т.и т.т., когда &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} отец вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов. Так как вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; точно принадлежит дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (свойство &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;), то она лежит на каком-то пути в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, причем все ее родители (центроиды) ее содержат. А по свойству &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; длина любого вертикального (и даже простого) пути есть &amp;lt;math&amp;gt;O(\log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Четвертое свойство очевидно из того, что &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; {{---}} дерево. Так как &amp;lt;math&amp;gt;T(u)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;T(v)&amp;lt;/math&amp;gt; {{---}} поддеревья различных вершин дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то либо они не пересекаются, либо &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;, либо &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Для доказательства последнего свойства в качестве вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; выберем &amp;lt;math&amp;gt;lca(u, v)&amp;lt;/math&amp;gt; в дереве центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Покажем, что так выбранная вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удовлетворяет заявленным свойствам. То, что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt; {{---}} очевидно по определению &amp;lt;math&amp;gt;lca&amp;lt;/math&amp;gt;, так как каждый предок любой вершины в дереве центроидов содержит эту вершину. Теперь докажем, что &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежит на пути между парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt;. Так как &amp;lt;math&amp;gt;c = lca(u, v)&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то из &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; нет ребра в такого сына, который содержит одновременно &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в своем поддереве (в дереве &amp;lt;math&amp;gt;T)&amp;lt;/math&amp;gt;, значит после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; разделится на несколько поддеревьев, таких что вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; окажутся в разных компонентах связности. А значит найдется такое ребро &amp;lt;math&amp;gt;(c, x)&amp;lt;/math&amp;gt;, которое принадлежало пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, но после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удалилось. Это доказывает то, что вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежала на пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
С помощью описанных свойств дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы можем решить задачу &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; для дерева:&lt;br /&gt;
&lt;br /&gt;
=== Пример решения задачи с помощью центроидной декомпозиции === &lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин. В каждый момент времени любая вершина дерева может быть либо помечена, либо нет. Изначально помечена только вершина номер &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Дан список из &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; запросов: &lt;br /&gt;
* Вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; пометили.&lt;br /&gt;
* С вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; сняли пометку.&lt;br /&gt;
* Найти номер ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины&amp;lt;ref&amp;gt;[http://neerc.ifmo.ru/school/camp-2016/problems/20160324a1.pdf  Сайт с задачами Санкт-Петербургских сборов к РОИ 2016]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Решение ====&lt;br /&gt;
&lt;br /&gt;
Построим центроидную декомпозицию &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Изначально посчитаем для каждого центроида, содержащего вершину &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt; посчитаем расстояние до вершины &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Для этого воспользуемся [[Метод_двоичного_подъема|методом двоичных подъемов для поиска lca пары вершин в дереве]], а также тем фактом, что если глубина &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве определена как расстояние от корня до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то длина пути между парой вершин &amp;lt;math&amp;gt;(u, v)&amp;lt;/math&amp;gt; есть &amp;lt;tex&amp;gt;len(u, v) = h(u) + h(v) - 2 \cdot h(lca(u, v))&amp;lt;/tex&amp;gt;. Если изначально предпосчитать проходом [[Обход_в_глубину,_цвета_вершин| &amp;lt;math&amp;gt;dfs&amp;lt;/math&amp;gt;]] величины &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, то ответ на запрос &amp;lt;math&amp;gt;len(u, v)&amp;lt;/math&amp;gt; можно делать за время &amp;lt;math&amp;gt;O(\log(n))&amp;lt;/math&amp;gt; с &amp;lt;tex&amp;gt;O(n \cdot \log(n)) &amp;lt;/tex&amp;gt;доп. памятью. Также можно воспользоваться техникой [[Сведение_задачи_LCA_к_задаче_RMQ|сведения задачи LCA к RMQ]] и решить с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; времени на запрос. Теперь научимся отвечать на запросы. Из последнего свойства центроидной декомпозиции видно, что если &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} искомая ближайшая помеченная вершина к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то путь между ними содержит центроид &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;, причем &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок одновременно вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве&amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Поэтому заведем [[Красно-черное_дерево|двоичное дерево поиска]] для каждого центроида &amp;lt;math&amp;gt;c \in T&amp;lt;/math&amp;gt;. В этой структуре для каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; будем хранить пары &amp;lt;math&amp;gt;(len(c, u), u)&amp;lt;/math&amp;gt; для всех помеченных вершин &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в поддереве центроидов &amp;lt;math&amp;gt;T(c)&amp;lt;/math&amp;gt;. Когда приходит запрос пометить вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} добавим в структуру данных для всех предков &amp;lt;math&amp;gt;p_c&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; пары &amp;lt;math&amp;gt;(len(p_c, v), v)&amp;lt;/math&amp;gt;. Мы совершим &amp;lt;math&amp;gt;O(\log(n))&amp;lt;/math&amp;gt; добавлений, затратив &amp;lt;math&amp;gt;O(\log(n))&amp;lt;/math&amp;gt; действий на каждое. Запрос снятия пометки с вершины обрабатывается аналогичными удалениями. Запрос поиска ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины {{---}} это запрос поиска вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, такой что  величина &amp;lt;math&amp;gt;len(c, u) + len(c, v)&amp;lt;/math&amp;gt; минимальна, где &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов (по пятому свойству, нас интересуют именно такие &amp;lt;math&amp;gt;c)&amp;lt;/math&amp;gt;. Этот запрос занимает так же &amp;lt;tex&amp;gt;O(log^2(n))&amp;lt;/tex&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
Итак, мы научились решать задачу с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и временной сложностью &amp;lt;math&amp;gt;O(log^2(n))&amp;lt;/math&amp;gt; на запрос любого типа с предварительным предпосчетом за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Варианты хранения центроидной декомпозиции ==&lt;br /&gt;
Для хранения дерева центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; есть &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; подхода, имеющих свои преимущества и недостатки:&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева запомним величину &amp;lt;math&amp;gt;p_v&amp;lt;/math&amp;gt; {{---}} номер предка вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Этот подход наиболее экономный по памяти &amp;lt;math&amp;gt;(O(n))&amp;lt;/math&amp;gt;, но уступает в скорости и функциональности.&lt;br /&gt;
&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева будем хранить весь массив предков &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt; в дереве центроидов.&lt;br /&gt;
&lt;br /&gt;
Этот подход уступает в количестве необходимой дополнительной памяти &amp;lt;tex&amp;gt;(O(n \cdot \log(n))&amp;lt;/tex&amp;gt; суммарно), но имеет ряд преимуществ:&lt;br /&gt;
&lt;br /&gt;
#При проходе по массиву предков фиксированной вершины будет выигрыш в скорости работы, так как весь массив будет лежать непрерывным блоком данных и следовательно будет закэширован&lt;br /&gt;
# На массиве предков можно строить различные структуры данных (такие как, например, дерево отрезков) для быстрого (в случае с деревом отрезков &amp;lt;math&amp;gt;O(log(\log(n)))&amp;lt;/math&amp;gt; на запрос) поиска предка с необходимыми свойствами. Так, например, в описанной выше задаче про помеченные вершины наибольшего общего предка можно искать методом двоичных подъемов за &amp;lt;math&amp;gt;O(log(\log(n))&amp;lt;/math&amp;gt; на запрос, так как размер массива предков есть &amp;lt;math&amp;gt;O(\log(n))&amp;lt;/math&amp;gt; (по свойству &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; центроидной декомпозиции). Используя эту оптимизацию можно получить время &amp;lt;math&amp;gt;O(\log(n)) + O(log(\log(n)) = O(\log(n))&amp;lt;/math&amp;gt; на запрос нахождения ближайшей помеченной вершины. Чтобы добиться улучшенной асимптотики для запросов изменения можно хранить дерево отрезков на каждом из путей &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt;, в каждой вершине которого хранить двоичное дерево поиска и поддерживать отложенные операции. Тогда ответ на эти запросы будет занимать &amp;lt;math&amp;gt;O(\log(n) \cdot log(\log(n))&amp;lt;/math&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
&lt;br /&gt;
*[[:Дерево_отрезков._Построение|Дерево отрезков]]&lt;br /&gt;
*[[:Heavy-light_декомпозиция|Heavy-light декомпозиция]]&lt;br /&gt;
*[[:Метод_двоичного_подъема| Метод двоичного подъема]]&lt;br /&gt;
&lt;br /&gt;
== Примечания == &lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Источники информации==&lt;br /&gt;
* [https://threads-iiith.quora.com/Centroid-Decomposition-of-a-Tree  Quora {{---}} Centroid Decomposition of a Tree]&lt;br /&gt;
* [http://acm.math.spbu.ru/~sk1/mm/lections/zksh2016-centroid/conspect.pdf  ЗКШ {{---}} Конспект лекции про центроидную декомпозицию]&lt;br /&gt;
* [https://neerc.ifmo.ru/school/archive/2014-2015/ru-olymp-roi-2015-analysis.pdf  Разбор задач РОИ 2015]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы и структуры данных]]&lt;/div&gt;</summary>
		<author><name>92.255.113.52</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61759</id>
		<title>Centroid decomposition</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61759"/>
				<updated>2017-06-22T17:22:53Z</updated>
		
		<summary type="html">&lt;p&gt;92.255.113.52: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Центроидная декомпозиция (англ. centroid decomposition) {{---}} это структура данных, позволяющая отвечать на запросы на дереве. Чаще всего это запросы, связанные с нахождением функции на вершинах, связанных неравенством на расстояние между ними в дереве. Также иногда применяется для запросов на путях в дереве.&lt;br /&gt;
&lt;br /&gt;
== Введение ==&lt;br /&gt;
Рассмотрим &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; задачи на обычном массиве (в дальнейшем мы будем их обобщать на случай дерева):&lt;br /&gt;
&lt;br /&gt;
Задача &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть массив &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; положительных целых чисел из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов. Также дано число &amp;lt;tex&amp;gt;W \geqslant 0&amp;lt;/tex&amp;gt; и число &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; индексов массива, таких что &amp;lt;tex&amp;gt;|j - i| \leqslant l &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\sum\limits_{k=i}^{j} a_k \leqslant W&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
Задача &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть прямая дорога, на которой расположены &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; городов. В некоторых городах есть госпитали, которые могут принимать больных. Поступают запросы вида:&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;, в котором находится больной и требуется найти такой город &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, который может принимать больных и &amp;lt;tex&amp;gt;|u - v|&amp;lt;/tex&amp;gt; минимально возможное.&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что больше он не будет принимать больных&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что теперь он может принимать больных&lt;br /&gt;
}}&lt;br /&gt;
Для начала решим обе задачи. &lt;br /&gt;
Первая задача решается методом [[Сортировка_слиянием|«разделяй и властвуй»]] {{---}} давайте разделим массив &amp;lt;tex&amp;gt;a[0 \dots n-1]&amp;lt;/tex&amp;gt; на 2 массива &amp;lt;tex&amp;gt;a[0\dots\dfrac{n}{2} - 1]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;a[\dfrac{n}{2} \dots n-1]&amp;lt;/tex&amp;gt; и рекурсивно решим задачу для каждого из них. Осталось научиться находить количество искомых пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt;, таких что &amp;lt;tex&amp;gt;i &amp;lt; \dfrac{n}{2},\text{ }j \geqslant \dfrac{n}{2}&amp;lt;/tex&amp;gt;. Для этого воспользуемся другой известной техникой {{---}} методом двух указателей. Посчитаем массив префиксных сумм для правой половины &amp;lt;tex&amp;gt;pref[i] = \sum\limits_{j=\frac{n}{2}}^{i} a_j&amp;lt;/tex&amp;gt; и суффиксных &amp;lt;tex&amp;gt;(suf[i] = \sum\limits_{j=i}^{\frac{n}{2} + 1} a_j)&amp;lt;/tex&amp;gt; {{---}} для левой. Заведем два указателя &amp;lt;tex&amp;gt;(p_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;p_2)&amp;lt;/tex&amp;gt;. Изначально установим &amp;lt;tex&amp;gt;p_1 = \dfrac{n}{2} - l + 1,\text{ }p_2 = \dfrac{n}{2}&amp;lt;/tex&amp;gt;. Пока &amp;lt;tex&amp;gt;p_2 - 1&amp;gt; \dfrac{n}{2}&amp;lt;/tex&amp;gt; и&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;pref[p_2] + suf[p_1] &amp;gt; W &amp;lt;/tex&amp;gt; будем уменьшать &amp;lt;math&amp;gt;p_2&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Если после этого &amp;lt;math&amp;gt;pref[p_2] + suf[p_1] \leqslant W&amp;lt;/math&amp;gt;, то к ответу прибавим &amp;lt;math&amp;gt;(p_2 - \dfrac{n}{2} + 1) \cdot (\dfrac{n}{2} - p_1)&amp;lt;/math&amp;gt;, после чего увеличим &amp;lt;math&amp;gt;p_1&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Так будем делать, пока &amp;lt;math&amp;gt;p_1 &amp;lt; \dfrac{n}{2}&amp;lt;/math&amp;gt;. В конце сложим текущий ответ и ответы для половин массива {{---}} получим ответ на задачу. Время работы такого алгоритма: &amp;lt;tex&amp;gt;T(n) = 2 \cdot T(n / 2) + O(n) = O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вторая задача имеет запросы на изменение и поэтому надо применить динамическую версию «разделяй и властвуй» {{---}} [[Дерево_отрезков._Построение|дерево отрезков]]. Построим дерево отрезков,&lt;br /&gt;
поддерживающее 2 вида запросов: присвоение в точке и минимум на отрезке. Изначально сделаем так, чтобы дереву отрезков соответствовал массив &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;b_i = 0&amp;lt;/tex&amp;gt;, если в &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м городе принимает госпиталь и &amp;lt;tex&amp;gt;b_i = 1&amp;lt;/tex&amp;gt; иначе. Когда в каком-то городе открывается/закрывается госпиталь {{---}} делаем запрос на изменение в дереве отрезков. Когда требуется узнать ближайший госпиталь к &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-му городу, можно воспользоваться одной из следующих идей: &lt;br /&gt;
* &amp;lt;tex&amp;gt;(O(n \cdot log^2(n))&amp;lt;/tex&amp;gt; Бинарным поиском ищем ближайший слева и ближайший справа к &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-му городу госпиталь (такой город &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, что &amp;lt;tex&amp;gt;\min\limits_{k=i..j}b_k= 1)&amp;lt;/tex&amp;gt;. Для этого внутри бинарного поиска каждый раз делаем запрос на поиск минимума в дереве отрезков.  &lt;br /&gt;
* &amp;lt;tex&amp;gt;(O(n \cdot \log(n))&amp;lt;/tex&amp;gt; Будем одним спуском/подъемом по дереву определять, куда нам нужно идти (в левое или правое поддерево), тем самым делая одновременно и бинарный поиск, и спуск/подъем по дереву.&lt;br /&gt;
&lt;br /&gt;
== Статическая центроидная декомпозиция ==&lt;br /&gt;
Перейдем к обобщению поставленных задач на случай дерева.&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть взвешенное дерево &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин, в каждой вершине которого написаны положительные целые числа. Также по-прежнему даны числа &amp;lt;tex&amp;gt;W \geqslant 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; вершин дерева, таких что расстояние между ними не превосходит &amp;lt;math&amp;gt;l&amp;lt;/math&amp;gt; по числу ребер и не превосходит &amp;lt;math&amp;gt;W&amp;lt;/math&amp;gt; по сумме весов&amp;lt;ref&amp;gt;[http://neerc.ifmo.ru/school/camp-2016/problems/20160324a1.pdf  Сайт с задачами Санкт-Петербургских сборов к РОИ 2016]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Для решения новой задачи применим ту же идею, что и была до этого {{---}} «разделяй и властвуй». Для этого нам потребуется следующий объект:&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|id=191213&lt;br /&gt;
|definition =&lt;br /&gt;
'''Центроидом дерева''' (англ. ''centroid'') называется такая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, после удаления которой дерево разбивается на несколько &amp;lt;math&amp;gt;(k)&amp;lt;/math&amp;gt; поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, таких что для каждого &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;: &amp;lt;tex&amp;gt;|t_i| \leqslant \dfrac{n}{2}&amp;lt;/tex&amp;gt;, то есть размер каждого поддерева не превосходит половины размера исходного дерева.&lt;br /&gt;
}}&lt;br /&gt;
Итак, в случае дерева идея «разделяй и властвуй» из предыдущего пункта будет формулироваться так: [[#l1|найдем центроид]]. Предположим, что мы сумели найти центроид за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; {{---}} размер дерева. Тогда, как и в упрощенной версии задачи {{---}} рекурсивно найдем ответ для всех поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, после чего попытаемся найти недостающие пары вершин, находящиеся в разных поддеревьях и удовлетворяющих вопросу задачи. Для этого будем отвечать на следующие запросы: пусть мы сейчас считаем все пары, где первая из вершин находится в поддереве &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и мы в некоторой структуре данных &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; храним все вершины остальных деревьев (каждую вершину задаем парой &amp;lt;math&amp;gt;(depth(v), length(v))&amp;lt;/math&amp;gt; {{---}} глубина вершины и длина пути до нее из корня поддерева), расстояние до которых от корня их поддерева не превышает &amp;lt;math&amp;gt;min(l, n)&amp;lt;/math&amp;gt;. Тогда просто пройдемся по всем вершинам &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; поддерева &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и прибавим к ответу число вершин в структуре &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt;, таких, что &amp;lt;tex&amp;gt;depth(u) \leqslant l - depth(v)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;length(u) \leqslant l - length(v)&amp;lt;/tex&amp;gt;. Это двумерные запросы, на которые можно отвечать за &amp;lt;math&amp;gt;O(log^2(n)&amp;lt;/math&amp;gt; с помощью [[Многомерное_дерево_отрезков|2d-дерева отрезков]], либо за &amp;lt;math&amp;gt;O(\log(n))&amp;lt;/math&amp;gt; с помощью [[Перечисление_точек_в_произвольном_прямоугольнике_за_n_*_log_%5E(d_-_1)_n_(range_tree)|техники поиска точек в d-мерном пространстве]]. Также читателю предлагается придумать и более эффективные и простые способы решить эту подзадачу.&lt;br /&gt;
&lt;br /&gt;
Оценим итоговое время работы алгоритма: &amp;lt;tex&amp;gt;T(n) = k \cdot T(n / k) + O(n \cdot \log(n))&amp;lt;/tex&amp;gt;. Решая это рекуррентное соотношение, получим &amp;lt;math&amp;gt;O(n \cdot \log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь докажем лемму о существовании центроида и опишем алгоритм его эффективного поиска.&lt;br /&gt;
&lt;br /&gt;
=== Лемма о существовании центроида и алгоритм его нахождения. ===&lt;br /&gt;
{{Лемма|id=l1&lt;br /&gt;
|statement=&lt;br /&gt;
В любом дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; существует центроид.&lt;br /&gt;
|proof=&lt;br /&gt;
Рассмотрим корень дерева &amp;lt;math&amp;gt;(r)&amp;lt;/math&amp;gt;. Положим изначально &amp;lt;math&amp;gt;v = r&amp;lt;/math&amp;gt;. Изначально &amp;lt;math&amp;gt;|subtree(v)| = n&amp;lt;/math&amp;gt;. Среди всех детей &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; выберем вершину &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; с максимальным размером поддерева. Если &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид, то положим &amp;lt;math&amp;gt;v = u&amp;lt;/math&amp;gt; и продолжим выбор нового &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, иначе {{---}} остановимся. Докажем, что мы в какой-то момент остановимся. Пусть в произвольный момент времени &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид и размер её наддерева меньше &amp;lt;math&amp;gt;\dfrac{n}{2}&amp;lt;/math&amp;gt;, значит максимальное поддерево имеет размер больше чем &amp;lt;math&amp;gt;\dfrac{n}{2}&amp;lt;/math&amp;gt;, то есть &amp;lt;math&amp;gt;|subtree(u)| &amp;gt; \dfrac{n}{2}&amp;lt;/math&amp;gt;, а значит размер &amp;quot;наддерева&amp;quot; вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; равен &amp;lt;tex&amp;gt;n - |subtree(u)| &amp;lt; \dfrac{n}{2}&amp;lt;/tex&amp;gt;. При этом теперь размер любого поддерева, на которое распадется дерево t при удалении вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; не превосходит &amp;lt;math&amp;gt;|subtree(u)| - 1&amp;lt;/math&amp;gt;, так как наддерево имеет размер меньше, чем поддерево &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, а любое поддерево вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; имеет хотя бы на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; вершину меньше (сама вершина &amp;lt;math&amp;gt;u)&amp;lt;/math&amp;gt;. По индукции получаем, что в любой момент времени размер наддерева вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; меньше &amp;lt;math&amp;gt;\dfrac{n}{2}&amp;lt;/math&amp;gt;, значит мы будем спускаться только вниз по дереву &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, и при переходе к вершине &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} сыну &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; размер максимального поддерева уменьшится как минимум на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Значит не более чем за &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; шагов наши действия прекратятся и мы окажемся в центроиде дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;.&lt;br /&gt;
Итак, мы конструктивно доказали существование центроида и привели линейный относительно размера дерева алгоритм его нахождения. &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Реализация ===&lt;br /&gt;
&lt;br /&gt;
Поиск центроида в дереве:&lt;br /&gt;
&lt;br /&gt;
  '''int''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int[]''' sz)&lt;br /&gt;
     max_subtree = -1&lt;br /&gt;
     '''for''' u : children(v)&lt;br /&gt;
          '''if''' sz[u] &amp;gt; sz[v] / 2 '''and''' sz[u] &amp;gt; sz[max_subtree] &amp;lt;font color=green&amp;gt;// считаем sz[-1] = 0 &amp;lt;/font &amp;gt;&lt;br /&gt;
               max_subtree = u&lt;br /&gt;
     '''if''' max_subtree == -1&lt;br /&gt;
          return v&lt;br /&gt;
     '''else''' &lt;br /&gt;
          '''return''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz)&lt;br /&gt;
&lt;br /&gt;
Шаблон решения произвольной задачи на статическую центроидную декомпозицию:&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int[]''' sz)&lt;br /&gt;
     c = &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz) &amp;lt;font color=green&amp;gt;// находим c {{---}} центроид поддерева вершины v &amp;lt;/font &amp;gt;&lt;br /&gt;
     ch = children[c]&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{deleteEdges}&amp;lt;/tex&amp;gt;(c, children) &amp;lt;font color=green&amp;gt;// удаляем все ребра между вершиной с и детьми, чтобы мы не смогли из детей попасть в с. &lt;br /&gt;
         Это полезно делать, если решение подзадачи для поддерева предполагает проход &amp;lt;math&amp;gt;dfs&amp;lt;/math&amp;gt; &amp;lt;/font &amp;gt;&lt;br /&gt;
     '''for''' c2 : ch[c]&lt;br /&gt;
          &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;(children, c2, sz)&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{mergeSolution}&amp;lt;/tex&amp;gt;(children, c2) &amp;lt;font color=green&amp;gt;// решаем для текущего поддерева &amp;lt;/font &amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Динамическая центроидная декомпозиция (дерево центроидной декомпозиции) ==&lt;br /&gt;
Теперь вернемся ко второй задаче введения. Для ее решения мы пользовались динамичекой версией «разделяй и властвуй» {{---}} деревом отрезков. В предыдущем пункте мы определили статический аналог «разделяй и властвуй» для дерева. Теперь обобщим этот метод для динамических задач.&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Деревом центроидов''' (или центроидной декомпозицией, англ. '''centroid decomposition tree''') дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; называют дерево &amp;lt;math&amp;gt;T(t)&amp;lt;/math&amp;gt;, построенное на вершинах дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; следующим образом:&lt;br /&gt;
* Пусть вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} центроид дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Объявим его корнем нового дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пусть при удалении вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; из дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; оно распалось на поддеревья &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, тогда детьми вершины c объявим &amp;lt;tex&amp;gt;T(t_1), T(t_t),\dots, T(t_k)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
=== Свойства центроидной декомпозиции ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
'''Свойства центроидной декомпозиции''':&lt;br /&gt;
# Глубина дерева центроидов не превосходит &amp;lt;tex&amp;gt;\\log(n)&amp;lt;/tex&amp;gt;. &lt;br /&gt;
# Каждая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; является центроидом одного из поддеревьев дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждая вершина дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; принадлежит &amp;lt;math&amp;gt;O(\log(n))&amp;lt;/math&amp;gt; поддеревьям дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (еще говорят, что вершина принадлежит &amp;lt;math&amp;gt;O(\log(n))&amp;lt;/math&amp;gt; центроидам дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, или что эти центроиды содержат вершину)&lt;br /&gt;
# Для любых вершин &amp;lt;tex&amp;gt;u, v \in T (u \neq v)&amp;lt;/tex&amp;gt; верно ровно одно из следующих трех утверждений: &lt;br /&gt;
#*&amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;&lt;br /&gt;
#* &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;&lt;br /&gt;
#* &amp;lt;tex&amp;gt;T(u) \cap T(v) = \emptyset &amp;lt;/tex&amp;gt;&lt;br /&gt;
# Простой путь между любой парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; содержит центроид &amp;lt;tex&amp;gt;c \in T(t)&amp;lt;/tex&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
# Действительно, так как размер поддерева &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; не превосходит &amp;lt;tex&amp;gt;\dfrac{|subtree(c)|}{2}&amp;lt;/tex&amp;gt;, то при спуске в каждую следующую вершину на пути к любому листу в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; размер поддерева вершины, в которой мы сейчас находимся, уменьшается как минимум на &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;. Значит длина всего пути до листа не превосходит &amp;lt;math&amp;gt;\log(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Второе свойство очевидно из построения дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, так как если вершина принадлежит дереву центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то она является центроидом, а из построения &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы знаем, что каждая вершина исходного дерева принадлежит и дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Третье свойство {{---}} прямое следствие первых двух, так как вершина принадлежит любому центроиду &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; т.и т.т., когда &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} отец вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов. Так как вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; точно принадлежит дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (свойство &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;), то она лежит на каком-то пути в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, причем все ее родители (центроиды) ее содержат. А по свойству &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; длина любого вертикального (и даже простого) пути есть &amp;lt;math&amp;gt;O(\log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Четвертое свойство очевидно из того, что &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; {{---}} дерево. Так как &amp;lt;math&amp;gt;T(u)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;T(v)&amp;lt;/math&amp;gt; {{---}} поддеревья различных вершин дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то либо они не пересекаются, либо &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;, либо &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Для доказательства последнего свойства в качестве вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; выберем &amp;lt;math&amp;gt;lca(u, v)&amp;lt;/math&amp;gt; в дереве центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Покажем, что так выбранная вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удовлетворяет заявленным свойствам. То, что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt; {{---}} очевидно по определению &amp;lt;math&amp;gt;lca&amp;lt;/math&amp;gt;, так как каждый предок любой вершины в дереве центроидов содержит эту вершину. Теперь докажем, что &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежит на пути между парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt;. Так как &amp;lt;math&amp;gt;c = lca(u, v)&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то из &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; нет ребра в такого сына, который содержит одновременно &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в своем поддереве (в дереве &amp;lt;math&amp;gt;T)&amp;lt;/math&amp;gt;, значит после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; разделится на несколько поддеревьев, таких что вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; окажутся в разных компонентах связности. А значит найдется такое ребро &amp;lt;math&amp;gt;(c, x)&amp;lt;/math&amp;gt;, которое принадлежало пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, но после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удалилось. Это доказывает то, что вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежала на пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
С помощью описанных свойств дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы можем решить задачу &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; для дерева:&lt;br /&gt;
&lt;br /&gt;
=== Пример решения задачи с помощью центроидной декомпозиции === &lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин. В каждый момент времени любая вершина дерева может быть либо помечена, либо нет. Изначально помечена только вершина номер &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Дан список из &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; запросов: &lt;br /&gt;
* Вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; пометили.&lt;br /&gt;
* С вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; сняли пометку.&lt;br /&gt;
* Найти номер ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины&amp;lt;ref&amp;gt;[http://neerc.ifmo.ru/school/camp-2016/problems/20160324a1.pdf  Сайт с задачами Санкт-Петербургских сборов к РОИ 2016]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Решение ====&lt;br /&gt;
&lt;br /&gt;
Построим центроидную декомпозицию &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Изначально посчитаем для каждого центроида, содержащего вершину &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt; посчитаем расстояние до вершины &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Для этого воспользуемся [[Метод_двоичного_подъема|методом двоичных подъемов для поиска lca пары вершин в дереве]], а также тем фактом, что если глубина &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве определена как расстояние от корня до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то длина пути между парой вершин &amp;lt;math&amp;gt;(u, v)&amp;lt;/math&amp;gt; есть &amp;lt;tex&amp;gt;len(u, v) = h(u) + h(v) - 2 \cdot h(lca(u, v))&amp;lt;/tex&amp;gt;. Если изначально предпосчитать проходом [[Обход_в_глубину,_цвета_вершин| &amp;lt;math&amp;gt;dfs&amp;lt;/math&amp;gt;]] величины &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, то ответ на запрос &amp;lt;math&amp;gt;len(u, v)&amp;lt;/math&amp;gt; можно делать за время &amp;lt;math&amp;gt;O(\log(n))&amp;lt;/math&amp;gt; с &amp;lt;tex&amp;gt;O(n \cdot \log(n)) &amp;lt;/tex&amp;gt;доп. памятью. Также можно воспользоваться техникой [[Сведение_задачи_LCA_к_задаче_RMQ|сведения задачи LCA к RMQ]] и решить с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; времени на запрос. Теперь научимся отвечать на запросы. Из последнего свойства центроидной декомпозиции видно, что если &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} искомая ближайшая помеченная вершина к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то путь между ними содержит центроид &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;, причем &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок одновременно вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве&amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Поэтому заведем [[Красно-черное_дерево|двоичное дерево поиска]] для каждого центроида &amp;lt;math&amp;gt;c \in T&amp;lt;/math&amp;gt;. В этой структуре для каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; будем хранить пары &amp;lt;math&amp;gt;(len(c, u), u)&amp;lt;/math&amp;gt; для всех помеченных вершин &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в поддереве центроидов &amp;lt;math&amp;gt;T(c)&amp;lt;/math&amp;gt;. Когда приходит запрос пометить вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} добавим в структуру данных для всех предков &amp;lt;math&amp;gt;p_c&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; пары &amp;lt;math&amp;gt;(len(p_c, v), v)&amp;lt;/math&amp;gt;. Мы совершим &amp;lt;math&amp;gt;O(\log(n))&amp;lt;/math&amp;gt; добавлений, затратив &amp;lt;math&amp;gt;O(\log(n))&amp;lt;/math&amp;gt; действий на каждое. Запрос снятия пометки с вершины обрабатывается аналогичными удалениями. Запрос поиска ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины {{---}} это запрос поиска вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, такой что  величина &amp;lt;math&amp;gt;len(c, u) + len(c, v)&amp;lt;/math&amp;gt; минимальна, где &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов (по пятому свойству, нас интересуют именно такие &amp;lt;math&amp;gt;c)&amp;lt;/math&amp;gt;. Этот запрос занимает так же &amp;lt;tex&amp;gt;O(log^2(n))&amp;lt;/tex&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
Итак, мы научились решать задачу с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и временной сложностью &amp;lt;math&amp;gt;O(log^2(n))&amp;lt;/math&amp;gt; на запрос любого типа с предварительным предпосчетом за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Варианты хранения центроидной декомпозиции ==&lt;br /&gt;
Для хранения дерева центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; есть &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; подхода, имеющих свои преимущества и недостатки:&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева запомним величину &amp;lt;math&amp;gt;p_v&amp;lt;/math&amp;gt; {{---}} номер предка вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Этот подход наиболее экономный по памяти &amp;lt;math&amp;gt;(O(n))&amp;lt;/math&amp;gt;, но уступает в скорости и функциональности.&lt;br /&gt;
&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева будем хранить весь массив предков &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt; в дереве центроидов.&lt;br /&gt;
&lt;br /&gt;
Этот подход уступает в количестве необходимой дополнительной памяти &amp;lt;tex&amp;gt;(O(n \cdot \log(n))&amp;lt;/tex&amp;gt; суммарно), но имеет ряд преимуществ:&lt;br /&gt;
&lt;br /&gt;
#При проходе по массиву предков фиксированной вершины будет выигрыш в скорости работы, так как весь массив будет лежать непрерывным блоком данных и следовательно будет закэширован&lt;br /&gt;
# На массиве предков можно строить различные структуры данных (такие как, например, дерево отрезков) для быстрого (в случае с деревом отрезков &amp;lt;math&amp;gt;O(log(\log(n)))&amp;lt;/math&amp;gt; на запрос) поиска предка с необходимыми свойствами. Так, например, в описанной выше задаче про помеченные вершины наибольшего общего предка можно искать методом двоичных подъемов за &amp;lt;math&amp;gt;O(log(\log(n))&amp;lt;/math&amp;gt; на запрос, так как размер массива предков есть &amp;lt;math&amp;gt;O(\log(n))&amp;lt;/math&amp;gt; (по свойству &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; центроидной декомпозиции). Используя эту оптимизацию можно получить время &amp;lt;math&amp;gt;O(\log(n)) + O(log(\log(n)) = O(\log(n))&amp;lt;/math&amp;gt; на запрос нахождения ближайшей помеченной вершины. Чтобы добиться улучшенной асимптотики для запросов изменения можно хранить дерево отрезков на каждом из путей &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt;, в каждой вершине которого хранить двоичное дерево поиска и поддерживать отложенные операции. Тогда ответ на эти запросы будет занимать &amp;lt;math&amp;gt;O(\log(n) \cdot log(\log(n))&amp;lt;/math&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
&lt;br /&gt;
*[[:Дерево_отрезков._Построение|Дерево отрезков]]&lt;br /&gt;
*[[:Heavy-light_декомпозиция|Heavy-light декомпозиция]]&lt;br /&gt;
*[[:Метод_двоичного_подъема| Метод двоичного подъема]]&lt;br /&gt;
&lt;br /&gt;
== Примечания == &lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Источники информации==&lt;br /&gt;
* [https://threads-iiith.quora.com/Centroid-Decomposition-of-a-Tree  Quora {{---}} Centroid Decomposition of a Tree]&lt;br /&gt;
* [http://acm.math.spbu.ru/~sk1/mm/lections/zksh2016-centroid/conspect.pdf  ЗКШ {{---}} Конспект лекции про центроидную декомпозицию]&lt;br /&gt;
* [https://neerc.ifmo.ru/school/archive/2014-2015/ru-olymp-roi-2015-analysis.pdf  Разбор задач РОИ 2015]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы и структуры данных]]&lt;/div&gt;</summary>
		<author><name>92.255.113.52</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61681</id>
		<title>Centroid decomposition</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61681"/>
				<updated>2017-06-18T18:49:43Z</updated>
		
		<summary type="html">&lt;p&gt;92.255.113.52: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Centroid decomposition (рус. центроидная декомпозиция) {{---}} это структура данных, позволяющая отвечать на запросы на дереве. Чаще всего это запросы, связанные с нахождением функции на вершинах, связанных неравенством на расстояние между ними в дереве. Также иногда применяется для запросов на путях в дереве.&lt;br /&gt;
&lt;br /&gt;
== Введение ==&lt;br /&gt;
Рассмотрим &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; задачи на обычном массиве (в дальнейшем мы будем их обобщать на случай дерева):&lt;br /&gt;
&lt;br /&gt;
Задача &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть массив &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; положительных целых чисел из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов. Также дано число &amp;lt;tex&amp;gt;W \geqslant 0&amp;lt;/tex&amp;gt; и число &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; индексов массива, таких что &amp;lt;tex&amp;gt;|j - i| \leqslant l &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\sum\limits_{k=i}^{j} a_k \leqslant W&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
Задача &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть прямая дорога, на которой расположены &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; городов. В некоторых городах есть госпитали, которые могут принимать больных. Поступают запросы вида:&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;, в котором находится больной и требуется найти такой город &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, который может принимать больных и &amp;lt;tex&amp;gt;|u - v|&amp;lt;/tex&amp;gt; минимально возможное.&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что больше он не будет принимать больных&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что теперь он может принимать больных&lt;br /&gt;
}}&lt;br /&gt;
Для начала решим обе задачи. &lt;br /&gt;
Первая задача решается методом [[Сортировка_слиянием|«разделяй и властвуй»]] {{---}} давайте разделим массив &amp;lt;tex&amp;gt;a[0 \dots n-1]&amp;lt;/tex&amp;gt; на 2 массива &amp;lt;tex&amp;gt;a[0\dots\dfrac{n}{2} - 1]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;a[\dfrac{n}{2} \dots n-1]&amp;lt;/tex&amp;gt; и рекурсивно решим задачу для каждого из них. Осталось научиться находить количество искомых пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt;, таких что &amp;lt;tex&amp;gt;i &amp;lt; \dfrac{n}{2},\text{ }j \geqslant \dfrac{n}{2}&amp;lt;/tex&amp;gt;. Для этого воспользуемся другой известной техникой {{---}} методом двух указателей. Посчитаем массив префиксных сумм для правой половины &amp;lt;tex&amp;gt;pref[i] = \sum\limits_{j=\frac{n}{2}}^{i} a_j&amp;lt;/tex&amp;gt; и суффиксных &amp;lt;tex&amp;gt;(suf[i] = \sum\limits_{j=i}^{\frac{n}{2} + 1} a_j)&amp;lt;/tex&amp;gt; {{---}} для левой. Заведем два указателя &amp;lt;tex&amp;gt;(p_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;p_2)&amp;lt;/tex&amp;gt;. Изначально установим &amp;lt;tex&amp;gt;p_1 = \dfrac{n}{2} - l + 1,\text{ }p_2 = \dfrac{n}{2}&amp;lt;/tex&amp;gt;. Пока &amp;lt;tex&amp;gt;p_2 - 1&amp;gt; \dfrac{n}{2}&amp;lt;/tex&amp;gt; и&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;pref[p_2] + suf[p_1] &amp;gt; W &amp;lt;/tex&amp;gt; будем уменьшать &amp;lt;math&amp;gt;p_2&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Если после этого &amp;lt;math&amp;gt;pref[p_2] + suf[p_1] \leqslant W&amp;lt;/math&amp;gt;, то к ответу прибавим &amp;lt;math&amp;gt;(p_2 - \dfrac{n}{2} + 1) \cdot (\dfrac{n}{2} - p_1)&amp;lt;/math&amp;gt;, после чего увеличим &amp;lt;math&amp;gt;p_1&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Так будем делать, пока &amp;lt;math&amp;gt;p_1 &amp;lt; \dfrac{n}{2}&amp;lt;/math&amp;gt;. В конце сложим текущий ответ и ответы для половин массива {{---}} получим ответ на задачу. Время работы такого алгоритма: &amp;lt;tex&amp;gt;T(n) = 2 \cdot T(n / 2) + O(n) = O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вторая задача имеет запросы на изменение и поэтому надо применить динамическую версию «разделяй и властвуй» {{---}} [[Дерево_отрезков._Построение|дерево отрезков]]. Построим дерево отрезков,&lt;br /&gt;
поддерживающее 2 вида запросов: присвоение в точке и минимум на отрезке. Изначально сделаем так, чтобы дереву отрезков соответствовал массив &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;b_i = 0&amp;lt;/tex&amp;gt;, если в &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м городе принимает госпиталь и &amp;lt;tex&amp;gt;b_i = 1&amp;lt;/tex&amp;gt; иначе. Когда в каком-то городе открывается/закрывается госпиталь {{---}} делаем запрос на изменение в дереве отрезков. Когда требуется узнать ближайший госпиталь к &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-му городу, можно воспользоваться одной из следующих идей: &lt;br /&gt;
* &amp;lt;tex&amp;gt;(O(n \cdot log^2(n))&amp;lt;/tex&amp;gt; Бинарным поиском ищем ближайший слева и ближайший справа к &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-му городу госпиталь (такой город &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, что &amp;lt;tex&amp;gt;\min\limits_{k=i..j}b_k= 1)&amp;lt;/tex&amp;gt;. Для этого внутри бинарного поиска каждый раз делаем запрос на поиск минимума в дереве отрезков.  &lt;br /&gt;
* &amp;lt;tex&amp;gt;(O(n \cdot \log(n))&amp;lt;/tex&amp;gt; Будем одним спуском/подъемом по дереву определять, куда нам нужно идти (в левое или правое поддерево), тем самым делая одновременно и бинарный поиск, и спуск/подъем по дереву.&lt;br /&gt;
&lt;br /&gt;
== Статическая центроидная декомпозиция ==&lt;br /&gt;
Перейдем к обобщению поставленных задач на случай дерева.&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть взвешенное дерево &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин, в каждой вершине которого написаны положительные целые числа. Также по-прежнему даны числа &amp;lt;tex&amp;gt;W \geqslant 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; вершин дерева, таких что расстояние между ними не превосходит &amp;lt;math&amp;gt;l&amp;lt;/math&amp;gt; по числу ребер и не превосходит &amp;lt;math&amp;gt;W&amp;lt;/math&amp;gt; по сумме весов&amp;lt;ref&amp;gt;[http://neerc.ifmo.ru/school/camp-2016/problems/20160324a1.pdf  Сайт с задачами Санкт-Петербургских сборов к РОИ 2016]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Для решения новой задачи применим ту же идею, что и была до этого {{---}} «разделяй и властвуй». Для этого нам потребуется следующий объект:&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|id=191213&lt;br /&gt;
|definition =&lt;br /&gt;
'''Центроидом дерева''' (англ. ''centroid'') называется такая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, после удаления которой дерево разбивается на несколько &amp;lt;math&amp;gt;(k)&amp;lt;/math&amp;gt; поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, таких что для каждого &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;: &amp;lt;tex&amp;gt;|t_i| \leqslant \dfrac{n}{2}&amp;lt;/tex&amp;gt;, то есть размер каждого поддерева не превосходит половины размера исходного дерева.&lt;br /&gt;
}}&lt;br /&gt;
Итак, в случае дерева идея «разделяй и властвуй» из предыдущего пункта будет формулироваться так: [[#l1|найдем центроид]]. Предположим, что мы сумели найти центроид за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; {{---}} размер дерева. Тогда, как и в упрощенной версии задачи {{---}} рекурсивно найдем ответ для всех поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, после чего попытаемся найти недостающие пары вершин, находящиеся в разных поддеревьях и удовлетворяющих вопросу задачи. Для этого будем отвечать на следующие запросы: пусть мы сейчас считаем все пары, где первая из вершин находится в поддереве &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и мы в некоторой структуре данных &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; храним все вершины остальных деревьев (каждую вершину задаем парой &amp;lt;math&amp;gt;(depth(v), length(v))&amp;lt;/math&amp;gt; {{---}} глубина вершины и длина пути до нее из корня поддерева), расстояние до которых от корня их поддерева не превышает &amp;lt;math&amp;gt;min(l, n)&amp;lt;/math&amp;gt;. Тогда просто пройдемся по всем вершинам &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; поддерева &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и прибавим к ответу число вершин в структуре &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt;, таких, что &amp;lt;tex&amp;gt;depth(u) \leqslant l - depth(v)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;length(u) \leqslant l - length(v)&amp;lt;/tex&amp;gt;. Это двумерные запросы, на которые можно отвечать за &amp;lt;math&amp;gt;O(log^2(n)&amp;lt;/math&amp;gt; с помощью [[Многомерное_дерево_отрезков|2d-дерева отрезков]], либо за &amp;lt;math&amp;gt;O(\log(n))&amp;lt;/math&amp;gt; с помощью [[Перечисление_точек_в_произвольном_прямоугольнике_за_n_*_log_%5E(d_-_1)_n_(range_tree)|техники поиска точек в d-мерном пространстве]]. Также читателю предлагается придумать и более эффективные и простые способы решить эту подзадачу.&lt;br /&gt;
&lt;br /&gt;
Оценим итоговое время работы алгоритма: &amp;lt;tex&amp;gt;T(n) = k \cdot T(n / k) + O(n \cdot \log(n))&amp;lt;/tex&amp;gt;. Решая это рекуррентное соотношение, получим &amp;lt;math&amp;gt;O(n \cdot \log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь докажем лемму о существовании центроида и опишем алгоритм его эффективного поиска.&lt;br /&gt;
&lt;br /&gt;
=== Лемма о существовании центроида и алгоритм его нахождения. ===&lt;br /&gt;
{{Лемма|id=l1&lt;br /&gt;
|statement=&lt;br /&gt;
В любом дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; существует центроид.&lt;br /&gt;
|proof=&lt;br /&gt;
Рассмотрим корень дерева &amp;lt;math&amp;gt;(r)&amp;lt;/math&amp;gt;. Положим изначально &amp;lt;math&amp;gt;v = r&amp;lt;/math&amp;gt;. Изначально &amp;lt;math&amp;gt;|subtree(v)| = n&amp;lt;/math&amp;gt;. Среди всех детей &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; выберем вершину &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; с максимальным размером поддерева. Если &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид, то положим &amp;lt;math&amp;gt;v = u&amp;lt;/math&amp;gt; и продолжим выбор нового &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, иначе {{---}} остановимся. Докажем, что мы в какой-то момент остановимся. Пусть в произвольный момент времени &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид и размер её наддерева меньше &amp;lt;math&amp;gt;\dfrac{n}{2}&amp;lt;/math&amp;gt;, значит максимальное поддерево имеет размер больше чем &amp;lt;math&amp;gt;\dfrac{n}{2}&amp;lt;/math&amp;gt;, то есть &amp;lt;math&amp;gt;|subtree(u)| &amp;gt; \dfrac{n}{2}&amp;lt;/math&amp;gt;, а значит размер &amp;quot;наддерева&amp;quot; вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; равен &amp;lt;tex&amp;gt;n - |subtree(u)| &amp;lt; \dfrac{n}{2}&amp;lt;/tex&amp;gt;. При этом теперь размер любого поддерева, на которое распадется дерево t при удалении вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; не превосходит &amp;lt;math&amp;gt;|subtree(u)| - 1&amp;lt;/math&amp;gt;, так как наддерево имеет размер меньше, чем поддерево &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, а любое поддерево вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; имеет хотя бы на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; вершину меньше (сама вершина &amp;lt;math&amp;gt;u)&amp;lt;/math&amp;gt;. По индукции получаем, что в любой момент времени размер наддерева вершины v меньше &amp;lt;math&amp;gt;\dfrac{n}{2}&amp;lt;/math&amp;gt;, значит мы будем спускаться только вниз по дереву &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, и при переходе к вершине &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} сыну &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; размер максимального поддерева уменьшится как минимум на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Значит не более чем за &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; шагов наши действия прекратятся и мы окажемся в центроиде дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;.&lt;br /&gt;
Итак, мы конструктивно доказали существование центроида и привели линейный относительно размера дерева алгоритм его нахождения. &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Реализация ===&lt;br /&gt;
&lt;br /&gt;
Поиск центроида в дереве:&lt;br /&gt;
&lt;br /&gt;
  '''int''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int[]''' sz)&lt;br /&gt;
     max_subtree = -1&lt;br /&gt;
     '''for''' u : children(v)&lt;br /&gt;
          '''if''' sz[u] &amp;gt; sz[v] / 2 '''and''' sz[u] &amp;gt; sz[max_subtree] &amp;lt;font color=green&amp;gt;// считаем sz[-1] = 0 &amp;lt;/font &amp;gt;&lt;br /&gt;
               max_subtree = u&lt;br /&gt;
     '''if''' max_subtree == -1&lt;br /&gt;
          return v&lt;br /&gt;
     '''else''' &lt;br /&gt;
          '''return''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz)&lt;br /&gt;
&lt;br /&gt;
Шаблон решения произвольной задачи на статическую центроидную декомпозицию:&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int[]''' sz)&lt;br /&gt;
     c = &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz) &amp;lt;font color=green&amp;gt;// находим c {{---}} центроид поддерева вершины v &amp;lt;/font &amp;gt;&lt;br /&gt;
     ch = children[c]&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{deleteEdges}&amp;lt;/tex&amp;gt;(c, children) &amp;lt;font color=green&amp;gt;// удаляем все ребра между вершиной с и детьми, чтобы мы не смогли из детей попасть в с. &lt;br /&gt;
         Это полезно делать, если решение подзадачи для поддерева предполагает проход &amp;lt;math&amp;gt;dfs&amp;lt;/math&amp;gt; &amp;lt;/font &amp;gt;&lt;br /&gt;
     '''for''' c2 : ch[c]&lt;br /&gt;
          &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;(children, c2, sz)&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{mergeSolution}&amp;lt;/tex&amp;gt;(children, c2) &amp;lt;font color=green&amp;gt;// решаем для текущего поддерева &amp;lt;/font &amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Динамическая центроидная декомпозиция (дерево центроидной декомпозиции) ==&lt;br /&gt;
Теперь вернемся ко второй задаче введения. Для ее решения мы пользовались динамичекой версией «разделяй и властвуй» {{---}} деревом отрезков. В предыдущем пункте мы определили статический аналог «разделяй и властвуй» для дерева. Теперь обобщим этот метод для динамических задач.&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Деревом центроидов (или центроидной декомпозицией, англ. centroid decomposition tree)’’' дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; называют дерево &amp;lt;math&amp;gt;T(t)&amp;lt;/math&amp;gt;, построенное на вершинах дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; следующим образом:&lt;br /&gt;
* Пусть вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} центроид дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Объявим его корнем нового дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пусть при удалении вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; из дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; оно распалось на поддеревья &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, тогда детьми вершины c объявим &amp;lt;tex&amp;gt;T(t_1), T(t_t),\dots, T(t_k)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
=== Свойства центроидной декомпозиции ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
'''Свойства центроидной декомпозиции''':&lt;br /&gt;
# Глубина дерева центроидов не превосходит &amp;lt;tex&amp;gt;\\log(n)&amp;lt;/tex&amp;gt;. &lt;br /&gt;
# Каждая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; является центроидом одного из поддеревьев дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждая вершина дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; принадлежит &amp;lt;math&amp;gt;O(\log(n))&amp;lt;/math&amp;gt; поддеревьям дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (еще говорят, что вершина принадлежит &amp;lt;math&amp;gt;O(\log(n))&amp;lt;/math&amp;gt; центроидам дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, или что эти центроиды содержат вершину)&lt;br /&gt;
# Для любых вершин &amp;lt;tex&amp;gt;u, v \in T (u \neq v)&amp;lt;/tex&amp;gt; верно ровно одно из следующих трех утверждений: &lt;br /&gt;
#*&amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;&lt;br /&gt;
#* &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;&lt;br /&gt;
#* &amp;lt;tex&amp;gt;T(u) \cap T(v) = \emptyset &amp;lt;/tex&amp;gt;&lt;br /&gt;
# Простой путь между любой парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; содержит центроид &amp;lt;tex&amp;gt;c \in T(t)&amp;lt;/tex&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
# Действительно, так как размер поддерева &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; не превосходит &amp;lt;tex&amp;gt;\dfrac{|subtree(c)|}{2}&amp;lt;/tex&amp;gt;, то при спуске в каждую следующую вершину на пути к любому листу в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; размер поддерева вершины, в которой мы сейчас находимся, уменьшается как минимум на &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;. Значит длина всего пути до листа не превосходит &amp;lt;math&amp;gt;\log(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Второе свойство очевидно из построения дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, так как если вершина принадлежит дереву центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то она является центроидом, а из построения &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы знаем, что каждая вершина исходного дерева принадлежит и дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Третье свойство {{---}} прямое следствие первых двух, так как вершина принадлежит любому центроиду &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; т.и т.т., когда &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} отец вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов. Так как вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; точно принадлежит дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (свойство &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;), то она лежит на каком-то пути в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, причем все ее родители (центроиды) ее содержат. А по свойству &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; длина любого вертикального (и даже простого) пути есть &amp;lt;math&amp;gt;O(\log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Четвертое свойство очевидно из того, что &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; {{---}} дерево. Так как &amp;lt;math&amp;gt;T(u)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;T(v)&amp;lt;/math&amp;gt; {{---}} поддеревья различных вершин дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то либо они не пересекаются, либо &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;, либо &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Для доказательства последнего свойства в качестве вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; выберем &amp;lt;math&amp;gt;lca(u, v)&amp;lt;/math&amp;gt; в дереве центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Покажем, что так выбранная вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удовлетворяет заявленным свойствам. То, что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt; {{---}} очевидно по определению &amp;lt;math&amp;gt;lca&amp;lt;/math&amp;gt;, так как каждый предок любой вершины в дереве центроидов содержит эту вершину. Теперь докажем, что &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежит на пути между парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt;. Так как &amp;lt;math&amp;gt;c = lca(u, v)&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то из &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; нет ребра в такого сына, который содержит одновременно &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в своем поддереве (в дереве &amp;lt;math&amp;gt;T)&amp;lt;/math&amp;gt;, значит после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; разделится на несколько поддеревьев, таких что вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; окажутся в разных компонентах связности. А значит найдется такое ребро &amp;lt;math&amp;gt;(c, x)&amp;lt;/math&amp;gt;, которое принадлежало пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, но после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удалилось. Это доказывает то, что вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежала на пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
С помощью описанных свойств дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы можем решить задачу 2 для дерева:&lt;br /&gt;
&lt;br /&gt;
=== Пример решения задачи с помощью центроидной декомпозиции === &lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин. В каждый момент времени любая вершина дерева может быть либо помечена, либо нет. Изначально помечена только вершина номер &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Дан список из &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; запросов: &lt;br /&gt;
* Вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; пометили.&lt;br /&gt;
* С вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; сняли пометку.&lt;br /&gt;
* Найти номер ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины&amp;lt;ref&amp;gt;[http://neerc.ifmo.ru/school/camp-2016/problems/20160324a1.pdf  Сайт с задачами Санкт-Петербургских сборов к РОИ 2016]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Решение ===&lt;br /&gt;
&lt;br /&gt;
Построим центроидную декомпозицию &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Изначально посчитаем для каждого центроида, содержащего вершину &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt; посчитаем расстояние до вершины &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Для этого воспользуемся [[Метод_двоичного_подъема|методом двоичных подъемов для поиска lca пары вершин в дереве]], а также тем фактом, что если глубина &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве определена как расстояние от корня до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то длина пути между парой вершин &amp;lt;math&amp;gt;(u, v)&amp;lt;/math&amp;gt; есть &amp;lt;tex&amp;gt;len(u, v) = h(u) + h(v) - 2 \cdot h(lca(u, v))&amp;lt;/tex&amp;gt;. Если изначально предпосчитать проходом [[Обход_в_глубину,_цвета_вершин| &amp;lt;math&amp;gt;dfs&amp;lt;/math&amp;gt;]] величины &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, то ответ на запрос &amp;lt;math&amp;gt;len(u, v)&amp;lt;/math&amp;gt; можно делать за время &amp;lt;math&amp;gt;O(\log(n))&amp;lt;/math&amp;gt; с &amp;lt;tex&amp;gt;O(n \cdot \log(n)) &amp;lt;/tex&amp;gt;доп. памятью. Также можно воспользоваться техникой [[Сведение_задачи_LCA_к_задаче_RMQ|сведения задачи LCA к RMQ]] и решить с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; времени на запрос. Теперь научимся отвечать на запросы. Из последнего свойства центроидной декомпозиции видно, что если &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} искомая ближайшая помеченная вершина к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то путь между ними содержит центроид &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;, причем &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок одновременно вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве&amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Поэтому заведем [[Красно-черное_дерево|двоичное дерево поиска]] для каждого центроида &amp;lt;math&amp;gt;c \in T&amp;lt;/math&amp;gt;. В этой структуре для каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; будем хранить пары &amp;lt;math&amp;gt;(len(c, u), u)&amp;lt;/math&amp;gt; для всех помеченных вершин &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в поддереве центроидов &amp;lt;math&amp;gt;T(c)&amp;lt;/math&amp;gt;. Когда приходит запрос пометить вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} добавим в структуру данных для всех предков &amp;lt;math&amp;gt;p_c&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; пары &amp;lt;math&amp;gt;(len(p_c, v), v)&amp;lt;/math&amp;gt;. Мы совершим &amp;lt;math&amp;gt;O(\log(n))&amp;lt;/math&amp;gt; добавлений, затратив &amp;lt;math&amp;gt;O(\log(n))&amp;lt;/math&amp;gt; действий на каждое. Запрос снятия пометки с вершины обрабатывается аналогичными удалениями. Запрос поиска ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины {{---}} это запрос поиска вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, такой что  величина &amp;lt;math&amp;gt;len(c, u) + len(c, v)&amp;lt;/math&amp;gt; минимальна, где &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов (по пятому свойству, нас интересуют именно такие &amp;lt;math&amp;gt;c)&amp;lt;/math&amp;gt;. Этот запрос занимает так же &amp;lt;tex&amp;gt;O(log^2(n))&amp;lt;/tex&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
Итак, мы научились решать задачу с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и временной сложностью &amp;lt;math&amp;gt;O(log^2(n))&amp;lt;/math&amp;gt; на запрос любого типа с предварительным предпосчетом за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Варианты хранения центроидной декомпозиции ==&lt;br /&gt;
Для хранения дерева центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; есть &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; подхода, имеющих свои преимущества и недостатки:&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева запомним величину &amp;lt;math&amp;gt;p_v&amp;lt;/math&amp;gt; {{---}} номер предка вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Этот подход наиболее экономный по памяти &amp;lt;math&amp;gt;(O(n))&amp;lt;/math&amp;gt;, но уступает в скорости и функциональности.&lt;br /&gt;
&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева будем хранить весь массив предков &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt; в дереве центроидов.&lt;br /&gt;
&lt;br /&gt;
Этот подход уступает в количестве необходимой дополнительной памяти &amp;lt;tex&amp;gt;(O(n \cdot \log(n))&amp;lt;/tex&amp;gt; суммарно), но имеет ряд преимуществ:&lt;br /&gt;
&lt;br /&gt;
#При проходе по массиву предков фиксированной вершины будет выигрыш в скорости работы, так как весь массив будет лежать непрерывным блоком данных и следовательно будет закэширован&lt;br /&gt;
# На массиве предков можно строить различные структуры данных (такие как, например, дерево отрезков) для быстрого (в случае с деревом отрезков &amp;lt;math&amp;gt;O(log(\log(n)))&amp;lt;/math&amp;gt; на запрос) поиска предка с необходимыми свойствами. Так, например, в описанной выше задаче про помеченные вершины наибольшего общего предка можно искать методом двоичных подъемов за &amp;lt;math&amp;gt;O(log(\log(n))&amp;lt;/math&amp;gt; на запрос, так как размер массива предков есть &amp;lt;math&amp;gt;O(\log(n))&amp;lt;/math&amp;gt; (по свойству &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; центроидной декомпозиции). Используя эту оптимизацию можно получить время &amp;lt;math&amp;gt;O(\log(n)) + O(log(\log(n)) = O(\log(n))&amp;lt;/math&amp;gt; на запрос нахождения ближайшей помеченной вершины. Чтобы добиться улучшенной асимптотики для запросов изменения можно хранить дерево отрезков на каждом из путей &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt;, в каждой вершине которого хранить двоичное дерево поиска и поддерживать отложенные операции. Тогда ответ на эти запросы будет занимать &amp;lt;math&amp;gt;O(\log(n) \cdot log(\log(n))&amp;lt;/math&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
&lt;br /&gt;
*[[:Дерево_отрезков._Построение|Дерево отрезков]]&lt;br /&gt;
*[[:Heavy-light_декомпозиция|Heavy-light декомпозиция]]&lt;br /&gt;
*[[:Метод_двоичного_подъема| Метод двоичного подъема]]&lt;br /&gt;
&lt;br /&gt;
== Примечания == &lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Источники информации==&lt;br /&gt;
* [https://threads-iiith.quora.com/Centroid-Decomposition-of-a-Tree  Quora {{---}} Centroid Decomposition of a Tree]&lt;br /&gt;
* [http://acm.math.spbu.ru/~sk1/mm/lections/zksh2016-centroid/conspect.pdf  ЗКШ {{---}} Конспект лекции про центроидную декомпозицию]&lt;br /&gt;
* [https://neerc.ifmo.ru/school/archive/2014-2015/ru-olymp-roi-2015-analysis.pdf  Разбор задач РОИ 2015]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы и структуры данных]]&lt;/div&gt;</summary>
		<author><name>92.255.113.52</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61680</id>
		<title>Centroid decomposition</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61680"/>
				<updated>2017-06-18T18:33:44Z</updated>
		
		<summary type="html">&lt;p&gt;92.255.113.52: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Centroid decomposition (рус. центроидная декомпозиция) {{---}} это структура данных, позволяющая отвечать на запросы на дереве. Чаще всего это запросы, связанные с нахождением функции на вершинах, связанных неравенством на расстояние между ними в дереве. Также иногда применяется для запросов на путях в дереве.&lt;br /&gt;
&lt;br /&gt;
== Введение ==&lt;br /&gt;
Рассмотрим &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; задачи на обычном массиве (в дальнейшем мы будем их обобщать на случай дерева):&lt;br /&gt;
&lt;br /&gt;
Задача &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть массив &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; положительных целых чисел из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов. Также дано число &amp;lt;tex&amp;gt;W \geqslant 0&amp;lt;/tex&amp;gt; и число &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; индексов массива, таких что &amp;lt;tex&amp;gt;|j - i| \leqslant l &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\sum\limits_{k=i}^{j} a_k \leqslant W&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
Задача &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть прямая дорога, на которой расположены &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; городов. В некоторых городах есть госпитали, которые могут принимать больных. Поступают запросы вида:&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;, в котором находится больной и требуется найти такой город &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, который может принимать больных и &amp;lt;tex&amp;gt;|u - v|&amp;lt;/tex&amp;gt; минимально возможное.&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что больше он не будет принимать больных&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что теперь он может принимать больных&lt;br /&gt;
}}&lt;br /&gt;
Для начала решим обе задачи. &lt;br /&gt;
Первая задача решается методом [[Сортировка_слиянием|«разделяй и властвуй»]] {{---}} давайте разделим массив &amp;lt;tex&amp;gt;a[0 \dots n-1]&amp;lt;/tex&amp;gt; на 2 массива &amp;lt;tex&amp;gt;a[0\dots\dfrac{n}{2} - 1]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;a[\dfrac{n}{2} \dots n-1]&amp;lt;/tex&amp;gt; и рекурсивно решим задачу для каждого из них. Осталось научиться находить количество искомых пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt;, таких что &amp;lt;tex&amp;gt;i &amp;lt; \dfrac{n}{2},\text{ }j \geqslant \dfrac{n}{2}&amp;lt;/tex&amp;gt;. Для этого воспользуемся другой известной техникой {{---}} методом двух указателей. Посчитаем массив префиксных сумм для правой половины &amp;lt;tex&amp;gt;pref[i] = \sum\limits_{j=\frac{n}{2}}^{i} a_j&amp;lt;/tex&amp;gt; и суффиксных &amp;lt;tex&amp;gt;(suf[i] = \sum\limits_{j=i}^{\frac{n}{2} + 1} a_j)&amp;lt;/tex&amp;gt; {{---}} для левой. Заведем два указателя &amp;lt;tex&amp;gt;(p_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;p_2)&amp;lt;/tex&amp;gt;. Изначально установим &amp;lt;tex&amp;gt;p_1 = \dfrac{n}{2} - l + 1,\text{ }p_2 = \dfrac{n}{2}&amp;lt;/tex&amp;gt;. Пока &amp;lt;tex&amp;gt;p_2 - 1&amp;gt; \dfrac{n}{2}&amp;lt;/tex&amp;gt; и&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;pref[p_2] + suf[p_1] &amp;gt; W &amp;lt;/tex&amp;gt; будем уменьшать &amp;lt;math&amp;gt;p_2&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Если после этого &amp;lt;math&amp;gt;pref[p_2] + suf[p_1] \leqslant W&amp;lt;/math&amp;gt;, то к ответу прибавим &amp;lt;math&amp;gt;(p_2 - \dfrac{n}{2} + 1) \cdot (\dfrac{n}{2} - p_1)&amp;lt;/math&amp;gt;, после чего увеличим &amp;lt;math&amp;gt;p_1&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Так будем делать, пока &amp;lt;math&amp;gt;p_1 &amp;lt; \dfrac{n}{2}&amp;lt;/math&amp;gt;. В конце сложим текущий ответ и ответы для половин массива {{---}} получим ответ на задачу. Время работы такого алгоритма: &amp;lt;tex&amp;gt;T(n) = 2 \cdot T(n / 2) + O(n) = O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вторая задача имеет запросы на изменение и поэтому надо применить динамическую версию «разделяй и властвуй» {{---}} [[Дерево_отрезков._Построение|дерево отрезков]]. Построим дерево отрезков,&lt;br /&gt;
поддерживающее 2 вида запросов: присвоение в точке и минимум на отрезке. Изначально сделаем так, чтобы дереву отрезков соответствовал массив &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;b_i = 0&amp;lt;/tex&amp;gt;, если в &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м городе принимает госпиталь и &amp;lt;tex&amp;gt;b_i = 1&amp;lt;/tex&amp;gt; иначе. Когда в каком-то городе открывается/закрывается госпиталь {{---}} делаем запрос на изменение в дереве отрезков. Когда требуется узнать ближайший госпиталь к &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-му городу, можно воспользоваться одной из следующих идей: &lt;br /&gt;
* &amp;lt;tex&amp;gt;(O(n \cdot log^2(n))&amp;lt;/tex&amp;gt; Бинарным поиском ищем ближайший слева и ближайший справа к &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-му городу госпиталь (такой город &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, что &amp;lt;tex&amp;gt;\min\limits_{k=i..j}b_k= 1)&amp;lt;/tex&amp;gt;. Для этого внутри бинарного поиска каждый раз делаем запрос на поиск минимума в дереве отрезков.  &lt;br /&gt;
* &amp;lt;tex&amp;gt;(O(n \cdot \log(n))&amp;lt;/tex&amp;gt; Будем одним спуском/подъемом по дереву определять, куда нам нужно идти (в левое или правое поддерево), тем самым делая одновременно и бинарный поиск, и спуск/подъем по дереву.&lt;br /&gt;
&lt;br /&gt;
== Статическая центроидная декомпозиция ==&lt;br /&gt;
Перейдем к обобщению поставленных задач на случай дерева.&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть взвешенное дерево &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин, в каждой вершине которого написаны положительные целые числа. Также по-прежнему даны числа &amp;lt;tex&amp;gt;W \geqslant 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; вершин дерева, таких что расстояние между ними не превосходит &amp;lt;math&amp;gt;l&amp;lt;/math&amp;gt; по числу ребер и не превосходит &amp;lt;math&amp;gt;W&amp;lt;/math&amp;gt; по сумме весов&amp;lt;ref&amp;gt;[http://neerc.ifmo.ru/school/camp-2016/problems/20160324a1.pdf  Сайт с задачами Санкт-Петербургских сборов к РОИ 2016]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Для решения новой задачи применим ту же идею, что и была до этого {{---}} &amp;quot;разделяй и властвуй&amp;quot;. Для этого нам потребуется следующий объект:&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|id=191213&lt;br /&gt;
|definition =&lt;br /&gt;
'''Центроидом дерева''' (англ. ''centroid'') называется такая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, после удаления которой дерево разбивается на несколько &amp;lt;math&amp;gt;(k)&amp;lt;/math&amp;gt; поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, таких что для каждого &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;: &amp;lt;tex&amp;gt;|t_i| \leqslant \dfrac{n}{2}&amp;lt;/tex&amp;gt;, то есть размер каждого поддерева не превосходит половины размера исходного дерева.&lt;br /&gt;
}}&lt;br /&gt;
Итак, в случае дерева идея &amp;quot;разделяй и властвуй&amp;quot; из предыдущего пункта будет формулироваться так: [[#l1|найдем центроид]]. Предположим, что мы сумели найти центроид за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; {{---}} размер дерева. Тогда, как и в упрощенной версии задачи {{---}} рекурсивно найдем ответ для всех поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, после чего попытаемся найти недостающие пары вершин, находящиеся в разных поддеревьях и удовлетворяющих вопросу задачи. Для этого будем отвечать на следующие запросы: пусть мы сейчас считаем все пары, где первая из вершин находится в поддереве &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и мы в некоторой структуре данных &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; храним все вершины остальных деревьев (каждую вершину задаем парой &amp;lt;math&amp;gt;(depth(v), length(v))&amp;lt;/math&amp;gt; {{---}} глубина вершины и длина пути до нее из корня поддерева), расстояние до которых от корня их поддерева не превышает &amp;lt;math&amp;gt;min(l, n)&amp;lt;/math&amp;gt;. Тогда просто пройдемся по всем вершинам &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; поддерева &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и прибавим к ответу число вершин в структуре &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt;, таких, что &amp;lt;tex&amp;gt;depth(u) \leqslant l - depth(v)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;length(u) \leqslant l - length(v)&amp;lt;/tex&amp;gt;. Это двумерные запросы, на которые можно отвечать за &amp;lt;math&amp;gt;O(log^2(n)&amp;lt;/math&amp;gt; с помощью [[Многомерное_дерево_отрезков|2d-дерева отрезков]], либо за &amp;lt;math&amp;gt;O(\log(n))&amp;lt;/math&amp;gt; с помощью [[Перечисление_точек_в_произвольном_прямоугольнике_за_n_*_log_%5E(d_-_1)_n_(range_tree)|техники поиска точек в d-мерном пространстве]]. Также читателю предлагается придумать и более эффективные и простые способы решить эту подзадачу.&lt;br /&gt;
&lt;br /&gt;
Оценим итоговое время работы алгоритма: &amp;lt;tex&amp;gt;T(n) = k \cdot T(n / k) + O(n \cdot \log(n))&amp;lt;/tex&amp;gt;. Решая это рекуррентное соотношение, получим &amp;lt;math&amp;gt;O(n \cdot \log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь докажем лемму о существовании центроида и опишем алгоритм его эффективного поиска.&lt;br /&gt;
&lt;br /&gt;
=== Лемма о существовании центроида и алгоритм его нахождения. ===&lt;br /&gt;
{{Лемма|id=l1&lt;br /&gt;
|statement=&lt;br /&gt;
В любом дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; существует центроид.&lt;br /&gt;
|proof=&lt;br /&gt;
Рассмотрим корень дерева &amp;lt;math&amp;gt;(r)&amp;lt;/math&amp;gt;. Положим изначально &amp;lt;math&amp;gt;v = r&amp;lt;/math&amp;gt;. Изначально &amp;lt;math&amp;gt;|subtree(v)| = n&amp;lt;/math&amp;gt;. Среди всех детей &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; выберем вершину &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; с максимальным размером поддерева. Если &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид, то положим &amp;lt;math&amp;gt;v = u&amp;lt;/math&amp;gt; и продолжим выбор нового &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, иначе {{---}} остановимся. Докажем, что мы в какой-то момент остановимся. Пусть в произвольный момент времени &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид и размер её наддерева меньше &amp;lt;math&amp;gt;\dfrac{n}{2}&amp;lt;/math&amp;gt;, значит максимальное поддерево имеет размер больше чем &amp;lt;math&amp;gt;\dfrac{n}{2}&amp;lt;/math&amp;gt;, то есть &amp;lt;math&amp;gt;|subtree(u)| &amp;gt; \dfrac{n}{2}&amp;lt;/math&amp;gt;, а значит размер &amp;quot;наддерева&amp;quot; вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; равен &amp;lt;tex&amp;gt;n - |subtree(u)| &amp;lt; \dfrac{n}{2}&amp;lt;/tex&amp;gt;. При этом теперь размер любого поддерева, на которое распадется дерево t при удалении вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; не превосходит &amp;lt;math&amp;gt;|subtree(u)| - 1&amp;lt;/math&amp;gt;, так как наддерево имеет размер меньше, чем поддерево &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, а любое поддерево вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; имеет хотя бы на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; вершину меньше (сама вершина &amp;lt;math&amp;gt;u)&amp;lt;/math&amp;gt;. По индукции получаем, что в любой момент времени размер наддерева вершины v меньше &amp;lt;math&amp;gt;\dfrac{n}{2}&amp;lt;/math&amp;gt;, значит мы будем спускаться только вниз по дереву &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, и при переходе к вершине &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} сыну &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; размер максимального поддерева уменьшится как минимум на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Значит не более чем за &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; шагов наши действия прекратятся и мы окажемся в центроиде дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;.&lt;br /&gt;
Итак, мы конструктивно доказали существование центроида и привели линейный относительно размера дерева алгоритм его нахождения. &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Реализация ===&lt;br /&gt;
&lt;br /&gt;
Поиск центроида в дереве:&lt;br /&gt;
&lt;br /&gt;
  '''int''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int[]''' sz)&lt;br /&gt;
     max_subtree = -1&lt;br /&gt;
     '''for''' u : children(v)&lt;br /&gt;
          '''if''' sz[u] &amp;gt; sz[v] / 2 '''and''' sz[u] &amp;gt; sz[max_subtree] &amp;lt;font color=green&amp;gt;// считаем sz[-1] = 0 &amp;lt;/font &amp;gt;&lt;br /&gt;
               max_subtree = u&lt;br /&gt;
     '''if''' max_subtree == -1&lt;br /&gt;
          return v&lt;br /&gt;
     '''else''' &lt;br /&gt;
          '''return''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz)&lt;br /&gt;
&lt;br /&gt;
Шаблон решения произвольной задачи на статическую центроидную декомпозицию:&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int[]''' sz)&lt;br /&gt;
     c = &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz) &amp;lt;font color=green&amp;gt;// находим c {{---}} центроид поддерева вершины v &amp;lt;/font &amp;gt;&lt;br /&gt;
     ch = children[c]&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{deleteEdges}&amp;lt;/tex&amp;gt;(c, children) &amp;lt;font color=green&amp;gt;// удаляем все ребра между вершиной с и детьми, чтобы мы не смогли из детей попасть в с. &lt;br /&gt;
         Это полезно делать, если решение подзадачи для поддерева предполагает проход &amp;lt;math&amp;gt;dfs&amp;lt;/math&amp;gt; &amp;lt;/font &amp;gt;&lt;br /&gt;
     '''for''' c2 : ch[c]&lt;br /&gt;
          &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;(children, c2, sz)&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{mergeSolution}&amp;lt;/tex&amp;gt;(children, c2) &amp;lt;font color=green&amp;gt;// решаем для текущего поддерева &amp;lt;/font &amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Динамическая центроидная декомпозиция (дерево центроидной декомпозиции) ==&lt;br /&gt;
Теперь вернемся ко второй задаче введения. Для ее решения мы пользовались динамичекой версией «разделяй и властвуй» {{---}} деревом отрезков. В предыдущем пункте мы определили статический аналог «разделяй и властвуй» для дерева. Теперь обобщим этот метод для динамических задач.&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Деревом центроидов (или центроидной декомпозицией, англ. centroid decomposition tree)’’' дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; называют дерево &amp;lt;math&amp;gt;T(t)&amp;lt;/math&amp;gt;, построенное на вершинах дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; следующим образом:&lt;br /&gt;
* Пусть вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} центроид дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Объявим его корнем нового дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пусть при удалении вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; из дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; оно распалось на поддеревья &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, тогда детьми вершины c объявим &amp;lt;tex&amp;gt;T(t_1), T(t_t),\dots, T(t_k)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
=== Свойства центроидной декомпозиции ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
'''Свойства центроидной декомпозиции''':&lt;br /&gt;
# Глубина дерева центроидов не превосходит &amp;lt;tex&amp;gt;\\log(n)&amp;lt;/tex&amp;gt;. &lt;br /&gt;
# Каждая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; является центроидом одного из поддеревьев дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждая вершина дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; принадлежит &amp;lt;math&amp;gt;O(\log(n))&amp;lt;/math&amp;gt; поддеревьям дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (еще говорят, что вершина принадлежит &amp;lt;math&amp;gt;O(\log(n))&amp;lt;/math&amp;gt; центроидам дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, или что эти центроиды содержат вершину)&lt;br /&gt;
# Для любых вершин &amp;lt;tex&amp;gt;u, v \in T (u \neq v)&amp;lt;/tex&amp;gt; верно ровно одно из следующих трех утверждений: &lt;br /&gt;
#*&amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;&lt;br /&gt;
#* &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;&lt;br /&gt;
#* &amp;lt;tex&amp;gt;T(u) \cap T(v) = \emptyset &amp;lt;/tex&amp;gt;&lt;br /&gt;
# Простой путь между любой парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; содержит центроид &amp;lt;tex&amp;gt;c \in T(t)&amp;lt;/tex&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
# Действительно, так как размер поддерева &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; не превосходит &amp;lt;tex&amp;gt;\dfrac{|subtree(c)|}{2}&amp;lt;/tex&amp;gt;, то при спуске в каждую следующую вершину на пути к любому листу в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; размер поддерева вершины, в которой мы сейчас находимся, уменьшается как минимум на &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;. Значит длина всего пути до листа не превосходит &amp;lt;math&amp;gt;\log(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Второе свойство очевидно из построения дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, так как если вершина принадлежит дереву центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то она является центроидом, а из построения &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы знаем, что каждая вершина исходного дерева принадлежит и дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Третье свойство {{---}} прямое следствие первых двух, так как вершина принадлежит любому центроиду &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; т.и т.т., когда &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} отец вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов. Так как вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; точно принадлежит дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (свойство &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;), то она лежит на каком-то пути в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, причем все ее родители (центроиды) ее содержат. А по свойству &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; длина любого вертикального (и даже простого) пути есть &amp;lt;math&amp;gt;O(\log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Четвертое свойство очевидно из того, что &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; {{---}} дерево. Так как &amp;lt;math&amp;gt;T(u)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;T(v)&amp;lt;/math&amp;gt; {{---}} поддеревья различных вершин дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то либо они не пересекаются, либо &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;, либо &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Для доказательства последнего свойства в качестве вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; выберем &amp;lt;math&amp;gt;lca(u, v)&amp;lt;/math&amp;gt; в дереве центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Покажем, что так выбранная вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удовлетворяет заявленным свойствам. То, что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt; {{---}} очевидно по определению &amp;lt;math&amp;gt;lca&amp;lt;/math&amp;gt;, так как каждый предок любой вершины в дереве центроидов содержит эту вершину. Теперь докажем, что &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежит на пути между парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt;. Так как &amp;lt;math&amp;gt;c = lca(u, v)&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то из &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; нет ребра в такого сына, который содержит одновременно &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в своем поддереве (в дереве &amp;lt;math&amp;gt;T)&amp;lt;/math&amp;gt;, значит после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; разделится на несколько поддеревьев, таких что вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; окажутся в разных компонентах связности. А значит найдется такое ребро &amp;lt;math&amp;gt;(c, x)&amp;lt;/math&amp;gt;, которое принадлежало пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, но после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удалилось. Это доказывает то, что вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежала на пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
С помощью описанных свойств дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы можем решить задачу 2 для дерева:&lt;br /&gt;
&lt;br /&gt;
=== Пример решения задачи с помощью центроидной декомпозиции === &lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин. В каждый момент времени любая вершина дерева может быть либо помечена, либо нет. Изначально помечена только вершина номер &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Дан список из &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; запросов: &lt;br /&gt;
* Вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; пометили.&lt;br /&gt;
* С вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; сняли пометку.&lt;br /&gt;
* Найти номер ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины&amp;lt;ref&amp;gt;[http://neerc.ifmo.ru/school/camp-2016/problems/20160324a1.pdf  Сайт с задачами Санкт-Петербургских сборов к РОИ 2016]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Решение ===&lt;br /&gt;
&lt;br /&gt;
Построим центроидную декомпозицию &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Изначально посчитаем для каждого центроида, содержащего вершину &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt; посчитаем расстояние до вершины &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Для этого воспользуемся [[Метод_двоичного_подъема|методом двоичных подъемов для поиска lca пары вершин в дереве]], а также тем фактом, что если глубина &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве определена как расстояние от корня до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то длина пути между парой вершин &amp;lt;math&amp;gt;(u, v)&amp;lt;/math&amp;gt; есть &amp;lt;tex&amp;gt;len(u, v) = h(u) + h(v) - 2 \cdot h(lca(u, v))&amp;lt;/tex&amp;gt;. Если изначально предпосчитать проходом [[Обход_в_глубину,_цвета_вершин| &amp;lt;math&amp;gt;dfs&amp;lt;/math&amp;gt;]] величины &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, то ответ на запрос &amp;lt;math&amp;gt;len(u, v)&amp;lt;/math&amp;gt; можно делать за время &amp;lt;math&amp;gt;O(\log(n))&amp;lt;/math&amp;gt; с &amp;lt;tex&amp;gt;O(n \cdot \log(n)) &amp;lt;/tex&amp;gt;доп. памятью. Также можно воспользоваться техникой [[Сведение_задачи_LCA_к_задаче_RMQ|сведения задачи LCA к RMQ]] и решить с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; времени на запрос. Теперь научимся отвечать на запросы. Из последнего свойства центроидной декомпозиции видно, что если &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} искомая ближайшая помеченная вершина к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то путь между ними содержит центроид &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;, причем &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок одновременно вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве&amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Поэтому заведем [[Красно-черное_дерево|двоичное дерево поиска]] для каждого центроида &amp;lt;math&amp;gt;c \in T&amp;lt;/math&amp;gt;. В этой структуре для каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; будем хранить пары &amp;lt;math&amp;gt;(len(c, u), u)&amp;lt;/math&amp;gt; для всех помеченных вершин &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в поддереве центроидов &amp;lt;math&amp;gt;T(c)&amp;lt;/math&amp;gt;. Когда приходит запрос пометить вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} добавим в структуру данных для всех предков &amp;lt;math&amp;gt;p_c&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; пары &amp;lt;math&amp;gt;(len(p_c, v), v)&amp;lt;/math&amp;gt;. Мы совершим &amp;lt;math&amp;gt;O(\log(n))&amp;lt;/math&amp;gt; добавлений, затратив &amp;lt;math&amp;gt;O(\log(n))&amp;lt;/math&amp;gt; действий на каждое. Запрос снятия пометки с вершины обрабатывается аналогичными удалениями. Запрос поиска ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины {{---}} это запрос поиска вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, такой что  величина &amp;lt;math&amp;gt;len(c, u) + len(c, v)&amp;lt;/math&amp;gt; минимальна, где &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов (по пятому свойству, нас интересуют именно такие &amp;lt;math&amp;gt;c)&amp;lt;/math&amp;gt;. Этот запрос занимает так же &amp;lt;tex&amp;gt;O(log^2(n))&amp;lt;/tex&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
Итак, мы научились решать задачу с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и временной сложностью &amp;lt;math&amp;gt;O(log^2(n))&amp;lt;/math&amp;gt; на запрос любого типа с предварительным предпосчетом за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Варианты хранения центроидной декомпозиции ==&lt;br /&gt;
Для хранения дерева центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; есть &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; подхода, имеющих свои преимущества и недостатки:&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева запомним величину &amp;lt;math&amp;gt;p_v&amp;lt;/math&amp;gt; {{---}} номер предка вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Этот подход наиболее экономный по памяти &amp;lt;math&amp;gt;(O(n))&amp;lt;/math&amp;gt;, но уступает в скорости и функциональности.&lt;br /&gt;
&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева будем хранить весь массив предков &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt; в дереве центроидов.&lt;br /&gt;
&lt;br /&gt;
Этот подход уступает в количестве необходимой дополнительной памяти &amp;lt;tex&amp;gt;(O(n \cdot \log(n))&amp;lt;/tex&amp;gt; суммарно), но имеет ряд преимуществ:&lt;br /&gt;
&lt;br /&gt;
#При проходе по массиву предков фиксированной вершины будет выигрыш в скорости работы, так как весь массив будет лежать непрерывным блоком данных и следовательно будет закэширован&lt;br /&gt;
# На массиве предков можно строить различные структуры данных (такие как, например, дерево отрезков) для быстрого (в случае с деревом отрезков &amp;lt;math&amp;gt;O(log(\log(n)))&amp;lt;/math&amp;gt; на запрос) поиска предка с необходимыми свойствами. Так, например, в описанной выше задаче про помеченные вершины наибольшего общего предка можно искать методом двоичных подъемов за &amp;lt;math&amp;gt;O(log(\log(n))&amp;lt;/math&amp;gt; на запрос, так как размер массива предков есть &amp;lt;math&amp;gt;O(\log(n))&amp;lt;/math&amp;gt; (по свойству &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; центроидной декомпозиции). Используя эту оптимизацию можно получить время &amp;lt;math&amp;gt;O(\log(n)) + O(log(\log(n)) = O(\log(n))&amp;lt;/math&amp;gt; на запрос нахождения ближайшей помеченной вершины. Чтобы добиться улучшенной асимптотики для запросов изменения можно хранить дерево отрезков на каждом из путей &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt;, в каждой вершине которого хранить двоичное дерево поиска и поддерживать отложенные операции. Тогда ответ на эти запросы будет занимать &amp;lt;math&amp;gt;O(\log(n) \cdot log(\log(n))&amp;lt;/math&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
&lt;br /&gt;
*[[:Дерево_отрезков._Построение|Дерево отрезков]]&lt;br /&gt;
*[[:Heavy-light_декомпозиция|Heavy-light декомпозиция]]&lt;br /&gt;
*[[:Метод_двоичного_подъема| Метод двоичного подъема]]&lt;br /&gt;
&lt;br /&gt;
== Примечания == &lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Источники информации==&lt;br /&gt;
* [https://threads-iiith.quora.com/Centroid-Decomposition-of-a-Tree  Quora {{---}} Centroid Decomposition of a Tree]&lt;br /&gt;
* [http://acm.math.spbu.ru/~sk1/mm/lections/zksh2016-centroid/conspect.pdf  ЗКШ {{---}} Конспект лекции про центроидную декомпозицию]&lt;br /&gt;
* [https://neerc.ifmo.ru/school/archive/2014-2015/ru-olymp-roi-2015-analysis.pdf  Разбор задач РОИ 2015]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы и структуры данных]]&lt;/div&gt;</summary>
		<author><name>92.255.113.52</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61647</id>
		<title>Centroid decomposition</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61647"/>
				<updated>2017-06-14T22:37:51Z</updated>
		
		<summary type="html">&lt;p&gt;92.255.113.52: /* Статическая центроидная декомпозиция */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Centroid decomposition (рус. центроидная декомпозиция) {{---}} это структура данных, позволяющая отвечать на запросы на дереве. Чаще всего это запросы, связанные с нахождением функции на вершинах, связанных неравенством на расстояние между ними в дереве. Также иногда применяется для запросов на путях в дереве.&lt;br /&gt;
&lt;br /&gt;
== Введение ==&lt;br /&gt;
Рассмотрим &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; задачи на обычном массиве (в дальнейшем мы будем их обобщать на случай дерева):&lt;br /&gt;
&lt;br /&gt;
Задача &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть массив &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; положительных целых чисел из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов. Также дано число &amp;lt;tex&amp;gt;W \geqslant 0&amp;lt;/tex&amp;gt; и число &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; индексов массива, таких что &amp;lt;tex&amp;gt;|j - i| \leqslant l &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\sum_{k=i}^{j} a_k \leqslant W&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
Задача &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть прямая дорога, на которой расположены &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; городов. В некоторых городах есть госпитали, которые могут принимать больных. Поступают запросы вида:&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;, в котором находится больной и требуется найти такой город &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; может принимать больных и &amp;lt;tex&amp;gt;|u - v|&amp;lt;/tex&amp;gt; минимально возможное.&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что больше он не будет принимать больных&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что теперь он может принимать больных&lt;br /&gt;
}}&lt;br /&gt;
Для начала решим обе задачи. &lt;br /&gt;
Первая задача решается методом [[Сортировка_слиянием|«разделяй и властвуй»]] {{---}} давайте разделим массив &amp;lt;tex&amp;gt;a[0 \dots n-1]&amp;lt;/tex&amp;gt; на 2 массива &amp;lt;tex&amp;gt;a[0\dots\dfrac{n}{2} - 1]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;a[\dfrac{n}{2} \dots n-1]&amp;lt;/tex&amp;gt; и рекурсивно решим задачу для каждого из них. Осталось научиться находить количество искомых пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt;, таких что &amp;lt;tex&amp;gt;i &amp;lt; \dfrac{n}{2}, j \geqslant \dfrac{n}{2}&amp;lt;/tex&amp;gt;. Для этого воспользуемся другой известной техникой {{---}} методом двух указателей. Посчитаем массив префиксных сумм для правой половины &amp;lt;tex&amp;gt;pref[i] = \sum_{j=\frac{n}{2}}^{i} a_j&amp;lt;/tex&amp;gt; и суффиксных (&amp;lt;tex&amp;gt;suf[i] = \sum_{j=i}^{\frac{n}{2} + 1} a_j&amp;lt;/tex&amp;gt;) {{---}} для левой. Заведем два указателя (&amp;lt;tex&amp;gt;p_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;p_2&amp;lt;/tex&amp;gt;). Изначально установим &amp;lt;tex&amp;gt;p_1 = \dfrac{n}{2} - l + 1, p_2 = \dfrac{n}{2}&amp;lt;/tex&amp;gt;. Пока &amp;lt;tex&amp;gt;p_2 - 1&amp;gt; \dfrac{n}{2}&amp;lt;/tex&amp;gt; и&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;pref[p_2] + suf[p_1] &amp;gt; W &amp;lt;/tex&amp;gt; будем уменьшать &amp;lt;math&amp;gt;p_2&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Если после этого &amp;lt;math&amp;gt;pref[p_2] + suf[p_1] \leqslant W&amp;lt;/math&amp;gt;, то к ответу прибавим &amp;lt;math&amp;gt;(p_2 - \dfrac{n}{2} + 1) * (\dfrac{n}{2} - p_1)&amp;lt;/math&amp;gt;, посго, увеличим &amp;lt;math&amp;gt;p_1&amp;lt;/math&amp;gt; на &amp;lt;math/math&amp;gt;. Так будем делать, пока &amp;lt;math&amp;gt;p_1 &amp;lt; \dfrac{n}{2}&amp;lt;/math&amp;gt;. В конце сложим текущий ответ и ответы для половин массива {{---}} получим ответ на задачу. Асимптотика такого алгоритма: &amp;lt;tex&amp;gt;T(n) = 2 * T(n / 2) + O(n) = O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вторая задача имеет запросы на изменение и поэтому надо применить динамическую версию «разделяй и властвуй» {{---}} [[Дерево_отрезков._Построение|дерево отрезков]]. Построим дерево отрезков,&lt;br /&gt;
поддерживающее 2 вида запросов: присвоение в точке и минимум на отрезке. Изначально сделаем так, чтобы дереву отрезков соответствовал массив &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;b_i = 0&amp;lt;/tex&amp;gt;, если в i-м городе принимает госпиталь и &amp;lt;tex&amp;gt;b_i = 1&amp;lt;/tex&amp;gt; иначе. Когда в каком-то городе открывается/закрывается госпиталь {{---}} делаем запрос на изменение в дереве отрезков. Когда требуется узнать ближайщий госпиталь к &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-му городу, можно воспользоваться одной из следующих идей: &lt;br /&gt;
а) (&amp;lt;tex&amp;gt;O(n \cdot log^2(n)&amp;lt;/tex&amp;gt;) Бинарным поиском ищем ближайший слева и ближайший справа к i-му городу госпиталь (такой город &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, что &amp;lt;tex&amp;gt;\min\limits_{k=i..j}b_k= 1&amp;lt;/tex&amp;gt;). Для этого внутри бинарного поиска каждый раз делаем запрос на поиск минимума в дереве отрезков.  &lt;br /&gt;
б) (&amp;lt;tex&amp;gt;O(n \cdot log(n)&amp;lt;/tex&amp;gt;) Будем одним спуском/подъемом по дереву определять, куда нам нужно идти (в левое или правое поддерево), тем самым делая одновоременно и бинарный поиск, и спуск/подъем по дереву.&lt;br /&gt;
&lt;br /&gt;
== Статическая центроидная декомпозиция ==&lt;br /&gt;
Перейдем к обобщению поставленных задач на случай дерева. Начнем, как и полагается, с первой:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть взвешенное дерево &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин, в каждой вершине которого написаны положительные целые числа. Также по-прежнему даны числа &amp;lt;tex&amp;gt;W \geqslant 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; вершин дерева, таких что расстояние между ними не превосходит &amp;lt;math&amp;gt;l&amp;lt;/math&amp;gt; по числу ребер и не превосходит &amp;lt;math&amp;gt;W&amp;lt;/math&amp;gt; по сумме весов.&lt;br /&gt;
}}&lt;br /&gt;
(Задача D со сборов СПБ к РОИ-2016&amp;lt;ref&amp;gt;[http://neerc.ifmo.ru/school/camp-2016/problems/20160324a1.pdf  Сайт с задачами Санкт-Петербургских сборов к РОИ 2016]&amp;lt;/ref&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
Для решения новой задачи применим ту же идею, что и была до этого {{---}} разделяй и властвуй. Для этого нам потребуется следующий объект:&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|id=191213&lt;br /&gt;
|definition =&lt;br /&gt;
'''Центроидом дерева''' (англ. ''centroid'') называется такая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, после удаления которой дерево разбивается на несколько (&amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;) поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, таких что для каждого &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;: &amp;lt;tex&amp;gt;|t_i| \leqslant \dfrac{n}{2}&amp;lt;/tex&amp;gt;, т.е. размер каждого поддерева не превосходит половины размера исходного дерева.&lt;br /&gt;
}}&lt;br /&gt;
Итак, в случае дерева идея разделяй-и-властвуй из предыдущего пункта будет формулироваться так: [[#l1|найдем центроид]]. Предположим, что мы сумели найти центроид за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; {{---}} размер дерева. Тогда, как и в упрощенной версии задачи {{---}} рекурсивно найдем ответ для всех поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, после чего попытаемся найти недостающие пары вершин, находящиехя в разных поддеревьях и удовлетворяющих вопросу задачи. Для этого будем отвечать на следующие запросы: пусть мы сейчас считаем все пары, где первая из вершин находится в поддереве &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и мы в некоторой структуре данных &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; храним все вершины остальных деревьев (каждую вершину задаем парой &amp;lt;math&amp;gt;(depth(v), length(v))&amp;lt;/math&amp;gt; {{---}} глубина вершины и длина пути до нее из корня поддерева), расстояние до которых от корня их поддерева не превышает &amp;lt;math&amp;gt;min(l, n)&amp;lt;/math&amp;gt;. Тогда просто пройдемся по всем вершинам &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; поддерева &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и прибавим к ответу число вершин в структуре &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt;, таких, что &amp;lt;tex&amp;gt;depth(u) \leqslant l - depth(v)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;length(u) \leqslant l - length(v)&amp;lt;/tex&amp;gt;. Это двумерные запросы, на которые можно отвечать за &amp;lt;math&amp;gt;O(log^2(n)&amp;lt;/math&amp;gt; с помощью [[Многомерное_дерево_отрезков|2d-дерева отрезков]], либо за &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с помощью [[Перечисление_точек_в_произвольном_прямоугольнике_за_n_*_log_%5E(d_-_1)_n_(range_tree)|техники поиска точек в d-мерном пространстве]]. Также читателю предлагается придумать и более эффективные и простые способы решить эту подзадачу.&lt;br /&gt;
&lt;br /&gt;
Оценим итоговую асимптотику: &amp;lt;tex&amp;gt;T(n) = k * T(n / k) + O(n \cdot log(n))&amp;lt;/tex&amp;gt;. Решая это рекурентное соотношение, получим &amp;lt;math&amp;gt;O(n \cdot log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь докажем лемму о существовании центроида и опишем алгоритм его эффективного поиска.&lt;br /&gt;
&lt;br /&gt;
=== Лемма о существовании центроида и алгоритм его нахождения. ===&lt;br /&gt;
{{Лемма|id=l1&lt;br /&gt;
|statement=&lt;br /&gt;
В любом дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; существует центроид.&lt;br /&gt;
|proof=&lt;br /&gt;
Рассмотрим корень дерева &amp;lt;math&amp;gt;(r)&amp;lt;/math&amp;gt;. Положим изначально &amp;lt;math&amp;gt;v = r&amp;lt;/math&amp;gt;. Изначально &amp;lt;math&amp;gt;|subtree(v)| = n&amp;lt;/math&amp;gt;. Среди всех детей &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; выберем вершину &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; с максимальным размером поддерева. Если &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид, то положим &amp;lt;math&amp;gt;v = u&amp;lt;/math&amp;gt; и продолжим выбор нового u, иначе {{---}} остановимся. Докажем, что мы в какой-то момент остановимся. Пусть в призвольный момент времени &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид и размер её наддерева меньше &amp;lt;math&amp;gt;\dfrac{n}{2}&amp;lt;/math&amp;gt;, значит максимальное поддерево имеет размер больше чем &amp;lt;math&amp;gt;\dfrac{n}{2}&amp;lt;/math&amp;gt;, т.е. &amp;lt;math&amp;gt;|subtree(u)| &amp;gt; \dfrac{n}{2}&amp;lt;/math&amp;gt;, а значит размер &amp;quot;наддерева&amp;quot; вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; равен &amp;lt;tex&amp;gt;n - |subtree(u)| &amp;lt; \dfrac{n}{2}&amp;lt;/tex&amp;gt;. При этом теперь размер любого поддерева, на которое распадется дерево t при удалении вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; не превосходит &amp;lt;math&amp;gt;|subtree(u)| - 1&amp;lt;/math&amp;gt;, т.к. наддерево имеет размер меньше, чем поддерево &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, а любое поддерево вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; имеет хотя бы на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; вершину меньше (сама вершина &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;). По индукции получаем, что в любой момент времени размер наддерева вершины v меньше &amp;lt;math&amp;gt;\dfrac{n}{2}&amp;lt;/math&amp;gt;, значит мы будем спускаться только вниз по дереву &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, и при переходе к вершине &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} сыну &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; размер максимального поддерева уменьшится как минимум на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Значит не более чем за &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; шагов наши действия прекратятся и мы окажемся в центроиде дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
Итак, мы конструктивно доказали существование центроида и привели линейный относительно размера дерева алгоритм его нахождения. &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Реализация ===&lt;br /&gt;
&lt;br /&gt;
Поиск центроида в дереве:&lt;br /&gt;
&lt;br /&gt;
  '''int''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int[]''' sz)&lt;br /&gt;
     max_subtree = -1&lt;br /&gt;
     '''for''' u : children(v)&lt;br /&gt;
          '''if''' sz[u] &amp;gt; sz[v] / 2 and sz[u] &amp;gt; sz[max_subtree] &amp;lt;font color=green&amp;gt;// считаем sz[-1] = 0 &amp;lt;/font &amp;gt;&lt;br /&gt;
               max_subtree = u&lt;br /&gt;
     '''if''' max_subtree == -1&lt;br /&gt;
          return v&lt;br /&gt;
     '''else''' &lt;br /&gt;
          '''return''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz)&lt;br /&gt;
&lt;br /&gt;
Шаблон решения произвольной задачи на статическую центроидную декомпозицию:&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int[]''' sz)&lt;br /&gt;
     c = &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz) &amp;lt;font color=green&amp;gt;// находим c {{---}} центроид поддерева вершины v &amp;lt;/font &amp;gt;&lt;br /&gt;
     ch = children[c]&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{deleteEdges}&amp;lt;/tex&amp;gt;(c, children) &amp;lt;font color=green&amp;gt;// удаляем все ребра между вершиной с и детьми, чтобы мы не смогли из детей попасть в с. &lt;br /&gt;
         Это полезно делать, если решение подзадачи для поддерева предполагает проход dfs &amp;lt;/font &amp;gt;&lt;br /&gt;
     '''for''' c2 : ch[c]&lt;br /&gt;
          &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;(children, c2, sz)&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{mergeSolution}&amp;lt;/tex&amp;gt;(children, c2) &amp;lt;font color=green&amp;gt;// решаем для текущего поддерева &amp;lt;/font &amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Динамическая центроидная декомпозиция (дерево центроидной декомпозиции) ==&lt;br /&gt;
Теперь вернемся ко второй задаче введения. Для ее решения мы пользовались динамичекой версией «разделяй и властвуй» {{---}} деревом отрезков. В предыдущем пункте мы определили статический аналог «разделяй и властвуй» для дерева. Теперь обобщим этот метод для динамических задач.&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Деревом центроидов (или центроидной декомпозицией)''' дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; называют дерево &amp;lt;math&amp;gt;T(t)&amp;lt;/math&amp;gt;, построенное на вершинах дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; следующим образом:&lt;br /&gt;
* Пусть вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} центроид дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Объявим его корнем нового дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пусть при удалении вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; из дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; оно распалось на поддеревья &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, тогда детьми вершины c объявим &amp;lt;tex&amp;gt;T(t_1), T(t_t),\dots, T(t_k)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
=== Свойства центроидной декомпозиции ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
'''Свойства центроидной декомпозиции''':&lt;br /&gt;
# Глубина дерева центроидов не превосходит &amp;lt;tex&amp;gt;log(n)&amp;lt;/tex&amp;gt;. &lt;br /&gt;
# Каждая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; является центроидом одного из поддеревьев дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждая вершина дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; поддеревьям дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (еще говорят, что вершина принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; центроидам дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, или что эти центроиды содержат вершину)&lt;br /&gt;
# Для любых вершин &amp;lt;tex&amp;gt;u, v \in T (u \neq v)&amp;lt;/tex&amp;gt; верно ровно одно из следующих трех утверждений: &lt;br /&gt;
#*&amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;&lt;br /&gt;
#* &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;&lt;br /&gt;
#* &amp;lt;tex&amp;gt;T(u) \cap T(v) = \emptyset &amp;lt;/tex&amp;gt;&lt;br /&gt;
# Простой путь между любой парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; содержит центроид &amp;lt;tex&amp;gt;c \in T(t)&amp;lt;/tex&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
# Действительно, т.к. размер поддерева &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; не превосходит &amp;lt;tex&amp;gt;\dfrac{|subtree(c)|}{2}&amp;lt;/tex&amp;gt;, то при спуске в каждую следующую вершину на пути к любому листу в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; размер поддерева вершины, в которой мы сейчас находимся, уменьшается как минимум на &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;. Значит длина всего пути до листа не превосходит &amp;lt;math&amp;gt;log(n)&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
# Второе свойство очевидно из построения дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, т.к. если вершина принадлежит дереву центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то она является центроидом, а из построения &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы знаем, что каждая вершина исходного дерева принадлежит и дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Третье свойство {{---}} прямое следствие первых двух, т.к. вершина принадлежит любому центроиду &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; т.и т.т., когда c {{---}} отец вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов. Т.к. вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; точно принадлежит дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (свойство 2), то она лежит на каком-то пути в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, причем все ее родители (центроиды) ее содержат. А по свойству 1 длина любого вертикального (и даже простого) пути есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
# Четвертое свойство очевидно из того, что &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; {{---}} дерево. Т.к. &amp;lt;math&amp;gt;T(u)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;T(v)&amp;lt;/math&amp;gt; {{---}} поддеревья различных вершин дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то либо они не пересекаются, либо &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;, либо &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Для доказательства последнего свойства выберем в качестве вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;lca(u, v)&amp;lt;/math&amp;gt; в дереве центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Покажем, что так выбранная вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удовлетворяет заявленным свойствам. То, что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt; {{---}} очевидно по определению &amp;lt;math&amp;gt;lca&amp;lt;/math&amp;gt;, т.к. каждый предок любой вершины в дереве центроидов содержит эту вершину. Теперь докажем, что &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежит на пути между парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt;. Т.к. &amp;lt;math&amp;gt;c = lca(u, v)&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то из &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; нет ребра в такого сына, который содержит одновременно &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в своем поддереве (в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;), значит после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; разделится на несколько поддеревьев, таких что вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; окажутся в разных компонентах связности. А значит найдется такое ребро &amp;lt;math&amp;gt;(c, x)&amp;lt;/math&amp;gt;, которое принадлежало пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, но после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удалилось. Это доказывает то, что вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежала на пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
С помощью описанных свойств дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы можем решить задачу 2 для дерева:&lt;br /&gt;
&lt;br /&gt;
=== Пример решения задачи с помощью центроидной декомпозиции === &lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин. В каждый момент времени любая вершина дерева может быть либо помечена, либо нет. Изначально помечена только вершина номер &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Дан список из &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; запросов: &lt;br /&gt;
* Вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; пометили.&lt;br /&gt;
* С вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; сняли пометку.&lt;br /&gt;
* Найти номер ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины.&lt;br /&gt;
}}&lt;br /&gt;
(Задача C со сборов СПБ к РОИ-2016&amp;lt;ref&amp;gt;[http://neerc.ifmo.ru/school/camp-2016/problems/20160324a1.pdf  Сайт с задачами Санкт-Петербургских сборов к РОИ 2016]&amp;lt;/ref&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
'''Решение:'''&lt;br /&gt;
&lt;br /&gt;
Построим центроидную декомпозицию &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Изначально посчитаем для каждого центроида, содержащего вершину &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt; посчитаем расстояние до вершины &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Для этого воспользуемся [[Метод_двоичного_подъема|методом двоичных подъемов для поиска lca пары вершин в дереве]], а также тем фактом, что если глубина &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве определена как расстояние от корня до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то длина пути между парой вершин &amp;lt;math&amp;gt;(u, v)&amp;lt;/math&amp;gt; есть &amp;lt;tex&amp;gt;len(u, v) = h(u) + h(v) - 2 \cdot h(lca(u, v))&amp;lt;/tex&amp;gt;. Если изначально предпосчитать проходом [[Обход_в_глубину,_цвета_вершин| dfs]] величины &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, то ответ на запрос &amp;lt;math&amp;gt;len(u, v)&amp;lt;/math&amp;gt; можно делать за время &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с &amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt;доп. памятью. Также можно воспользоваться техникой [[Сведение_задачи_LCA_к_задаче_RMQ|сведения задачи LCA к RMQ]] и решить с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; времени на запрос. Теперь научимся отвечать на запросы. Из последнего свойства центроидной декомпозиции видно, что если &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} искомая ближайшая помеченная вершина к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то путь между ними содержит центроид &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;, причем &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок одновременно вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве&amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Поэтому заведем [[Красно-черное_дерево|двоичное дерево поиска]] для каждого центроида &amp;lt;math&amp;gt;c \in T&amp;lt;/math&amp;gt;. В этой структуре для каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; будем хранить пары &amp;lt;math&amp;gt;(len(c, u), u)&amp;lt;/math&amp;gt; для всех помеченных вершин &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в поддереве центроидов &amp;lt;math&amp;gt;T(c)&amp;lt;/math&amp;gt;. Когда приходит запрос пометить вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} добавим в структуру данных для всех предков &amp;lt;math&amp;gt;p_c&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; пары &amp;lt;math&amp;gt;(len(p_c, v), v)&amp;lt;/math&amp;gt;. Мы совершим &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; добавлений, затратив &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; действий на каждое. Запрос снятия пометки с вершины обрабатывается аналогичными удалениями. Запрос поиска ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины {{---}} это запрос поиска вершины u, такой что  величина &amp;lt;math&amp;gt;len(c, u) + len(c, v)&amp;lt;/math&amp;gt; минимальна, где &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов (по пятому свойству, нас интересуют именно такие &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;). Этот запрос занимает так же &amp;lt;tex&amp;gt;O(log^2(n))&amp;lt;/tex&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
Итак, мы научились решать задачу с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и временной сложностью &amp;lt;math&amp;gt;O(log^2(n))&amp;lt;/math&amp;gt; на запрос любого типа с предварительным предпосчетом за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Варианты хранения центроидной декомпозиции ==&lt;br /&gt;
Для хранения дерева центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; есть &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; подхода, имеющих свои преимущества и недостатки:&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева запомним величину &amp;lt;math&amp;gt;p_v&amp;lt;/math&amp;gt; {{---}} номер предка вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Этот подход наиболее экономный по памяти (&amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;), но уступает в скорости и функциональности.&lt;br /&gt;
&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева будем хранить весь массив предков &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt; в дереве центроидов.&lt;br /&gt;
&lt;br /&gt;
Этот подход уступает в количестве необходимой дополнительной памяти (&amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt; суммарно), но имеет ряд преимуществ:&lt;br /&gt;
&lt;br /&gt;
#При проходе по массиву предков фиксированной вершины будет выигрыш в скорости работы, т.к. весь массив будет лежать непрерывным блоком данных и следовательно будет закэширован&lt;br /&gt;
# На массиве предков можно строить различные структуры данных (такие как, например, дерево отрезков) для быстрого (в случае с деревом отрезков &amp;lt;math&amp;gt;O(log(log(n)))&amp;lt;/math&amp;gt; на запрос) поиска предка с необходимыми свойствами. Так, например, в описанной выше задаче про помеченные вершины наибольшего общего предка можно искать методом двоичных подъемов за &amp;lt;math&amp;gt;O(log(log(n))&amp;lt;/math&amp;gt; на запрос, т.к. размер массива предков есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; (по свойству 1 центроидной декомпозиции). Используя эту оптимизацию можно получить время &amp;lt;math&amp;gt;O(log(n)) + O(log(log(n)) = O(log(n))&amp;lt;/math&amp;gt; на запрос нахождения ближайшей помеченной вершины. Чтобы добиться улучшенной асимптотики для запросов изменения можно хранить дерево отрезков на каждом из путей &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt;, в каждой вершине которого хранить двоичное дерево поиска и поддерживать отложенные операции. Тогда ответ на эти запросы будет занимать &amp;lt;math&amp;gt;O(log(n) \cdot log(log(n))&amp;lt;/math&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
&lt;br /&gt;
*[[:Дерево_отрезков._Построение|Дерево отрезков]]&lt;br /&gt;
*[[:Heavy-light_декомпозиция|Heavy-light декомпозиция]]&lt;br /&gt;
*[[:Метод_двоичного_подъема| Метод двоичного подъема]]&lt;br /&gt;
&lt;br /&gt;
== Примечания == &lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы и структуры данных]]&lt;/div&gt;</summary>
		<author><name>92.255.113.52</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61646</id>
		<title>Centroid decomposition</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61646"/>
				<updated>2017-06-14T22:36:38Z</updated>
		
		<summary type="html">&lt;p&gt;92.255.113.52: /* Лемма о существовании центроида и алгоритм его нахождения. */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Centroid decomposition (рус. центроидная декомпозиция) {{---}} это структура данных, позволяющая отвечать на запросы на дереве. Чаще всего это запросы, связанные с нахождением функции на вершинах, связанных неравенством на расстояние между ними в дереве. Также иногда применяется для запросов на путях в дереве.&lt;br /&gt;
&lt;br /&gt;
== Введение ==&lt;br /&gt;
Рассмотрим &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; задачи на обычном массиве (в дальнейшем мы будем их обобщать на случай дерева):&lt;br /&gt;
&lt;br /&gt;
Задача &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть массив &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; положительных целых чисел из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов. Также дано число &amp;lt;tex&amp;gt;W \geqslant 0&amp;lt;/tex&amp;gt; и число &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; индексов массива, таких что &amp;lt;tex&amp;gt;|j - i| \leqslant l &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\sum_{k=i}^{j} a_k \leqslant W&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
Задача &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть прямая дорога, на которой расположены &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; городов. В некоторых городах есть госпитали, которые могут принимать больных. Поступают запросы вида:&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;, в котором находится больной и требуется найти такой город &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; может принимать больных и &amp;lt;tex&amp;gt;|u - v|&amp;lt;/tex&amp;gt; минимально возможное.&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что больше он не будет принимать больных&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что теперь он может принимать больных&lt;br /&gt;
}}&lt;br /&gt;
Для начала решим обе задачи. &lt;br /&gt;
Первая задача решается методом [[Сортировка_слиянием|«разделяй и властвуй»]] {{---}} давайте разделим массив &amp;lt;tex&amp;gt;a[0 \dots n-1]&amp;lt;/tex&amp;gt; на 2 массива &amp;lt;tex&amp;gt;a[0\dots\dfrac{n}{2} - 1]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;a[\dfrac{n}{2} \dots n-1]&amp;lt;/tex&amp;gt; и рекурсивно решим задачу для каждого из них. Осталось научиться находить количество искомых пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt;, таких что &amp;lt;tex&amp;gt;i &amp;lt; \dfrac{n}{2}, j \geqslant \dfrac{n}{2}&amp;lt;/tex&amp;gt;. Для этого воспользуемся другой известной техникой {{---}} методом двух указателей. Посчитаем массив префиксных сумм для правой половины &amp;lt;tex&amp;gt;pref[i] = \sum_{j=\frac{n}{2}}^{i} a_j&amp;lt;/tex&amp;gt; и суффиксных (&amp;lt;tex&amp;gt;suf[i] = \sum_{j=i}^{\frac{n}{2} + 1} a_j&amp;lt;/tex&amp;gt;) {{---}} для левой. Заведем два указателя (&amp;lt;tex&amp;gt;p_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;p_2&amp;lt;/tex&amp;gt;). Изначально установим &amp;lt;tex&amp;gt;p_1 = \dfrac{n}{2} - l + 1, p_2 = \dfrac{n}{2}&amp;lt;/tex&amp;gt;. Пока &amp;lt;tex&amp;gt;p_2 - 1&amp;gt; \dfrac{n}{2}&amp;lt;/tex&amp;gt; и&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;pref[p_2] + suf[p_1] &amp;gt; W &amp;lt;/tex&amp;gt; будем уменьшать &amp;lt;math&amp;gt;p_2&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Если после этого &amp;lt;math&amp;gt;pref[p_2] + suf[p_1] \leqslant W&amp;lt;/math&amp;gt;, то к ответу прибавим &amp;lt;math&amp;gt;(p_2 - \dfrac{n}{2} + 1) * (\dfrac{n}{2} - p_1)&amp;lt;/math&amp;gt;, посго, увеличим &amp;lt;math&amp;gt;p_1&amp;lt;/math&amp;gt; на &amp;lt;math/math&amp;gt;. Так будем делать, пока &amp;lt;math&amp;gt;p_1 &amp;lt; \dfrac{n}{2}&amp;lt;/math&amp;gt;. В конце сложим текущий ответ и ответы для половин массива {{---}} получим ответ на задачу. Асимптотика такого алгоритма: &amp;lt;tex&amp;gt;T(n) = 2 * T(n / 2) + O(n) = O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вторая задача имеет запросы на изменение и поэтому надо применить динамическую версию «разделяй и властвуй» {{---}} [[Дерево_отрезков._Построение|дерево отрезков]]. Построим дерево отрезков,&lt;br /&gt;
поддерживающее 2 вида запросов: присвоение в точке и минимум на отрезке. Изначально сделаем так, чтобы дереву отрезков соответствовал массив &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;b_i = 0&amp;lt;/tex&amp;gt;, если в i-м городе принимает госпиталь и &amp;lt;tex&amp;gt;b_i = 1&amp;lt;/tex&amp;gt; иначе. Когда в каком-то городе открывается/закрывается госпиталь {{---}} делаем запрос на изменение в дереве отрезков. Когда требуется узнать ближайщий госпиталь к &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-му городу, можно воспользоваться одной из следующих идей: &lt;br /&gt;
а) (&amp;lt;tex&amp;gt;O(n \cdot log^2(n)&amp;lt;/tex&amp;gt;) Бинарным поиском ищем ближайший слева и ближайший справа к i-му городу госпиталь (такой город &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, что &amp;lt;tex&amp;gt;\min\limits_{k=i..j}b_k= 1&amp;lt;/tex&amp;gt;). Для этого внутри бинарного поиска каждый раз делаем запрос на поиск минимума в дереве отрезков.  &lt;br /&gt;
б) (&amp;lt;tex&amp;gt;O(n \cdot log(n)&amp;lt;/tex&amp;gt;) Будем одним спуском/подъемом по дереву определять, куда нам нужно идти (в левое или правое поддерево), тем самым делая одновоременно и бинарный поиск, и спуск/подъем по дереву.&lt;br /&gt;
&lt;br /&gt;
== Статическая центроидная декомпозиция ==&lt;br /&gt;
Перейдем к обобщению поставленных задач на случай дерева. Начнем, как и полагается, с первой:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть взвешенное дерево &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин, в каждой вершине которого написаны положительные целые числа. Также по-прежнему даны числа &amp;lt;tex&amp;gt;W \geqslant 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; вершин дерева, таких что расстояние между ними не превосходит &amp;lt;math&amp;gt;l&amp;lt;/math&amp;gt; по числу ребер и не превосходит &amp;lt;math&amp;gt;W&amp;lt;/math&amp;gt; по сумме весов.&lt;br /&gt;
}}&lt;br /&gt;
(Задача D со сборов СПБ к РОИ-2016&amp;lt;ref&amp;gt;[http://neerc.ifmo.ru/school/camp-2016/problems/20160324a1.pdf  Сайт с задачами Санкт-Петербургских сборов к РОИ 2016]&amp;lt;/ref&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
Для решения новой задачи применим ту же идею, что и была до этого {{---}} разделяй и властвуй. Для этого нам потребуется следующий объект:&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|id=191213&lt;br /&gt;
|definition =&lt;br /&gt;
'''Центроидом дерева''' (англ. ''centroid'') называется такая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, после удаления которой дерево разбивается на несколько (&amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;) поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, таких что для каждого &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;: &amp;lt;tex&amp;gt;|t_i| \leqslant \dfrac{n}{2}&amp;lt;/tex&amp;gt;, т.е. размер каждого поддерева не превосходит половины размера исходного дерева.&lt;br /&gt;
}}&lt;br /&gt;
Итак, в случае дерева идея разделяй-и-властвуй из предыдущего пункта будет формулироваться так: найдем центроид. Предположим, что мы сумели найти центроид за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; {{---}} размер дерева. Тогда, как и в упрощенной версии задачи {{---}} рекурсивно найдем ответ для всех поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, после чего попытаемся найти недостающие пары вершин, находящиехя в разных поддеревьях и удовлетворяющих вопросу задачи. Для этого будем отвечать на следующие запросы: пусть мы сейчас считаем все пары, где первая из вершин находится в поддереве &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и мы в некоторой структуре данных &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; храним все вершины остальных деревьев (каждую вершину задаем парой &amp;lt;math&amp;gt;(depth(v), length(v))&amp;lt;/math&amp;gt; {{---}} глубина вершины и длина пути до нее из корня поддерева), расстояние до которых от корня их поддерева не превышает &amp;lt;math&amp;gt;min(l, n)&amp;lt;/math&amp;gt;. Тогда просто пройдемся по всем вершинам &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; поддерева &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и прибавим к ответу число вершин в структуре &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt;, таких, что &amp;lt;tex&amp;gt;depth(u) \leqslant l - depth(v)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;length(u) \leqslant l - length(v)&amp;lt;/tex&amp;gt;. Это двумерные запросы, на которые можно отвечать за &amp;lt;math&amp;gt;O(log^2(n)&amp;lt;/math&amp;gt; с помощью [[Многомерное_дерево_отрезков|2d-дерева отрезков]], либо за &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с помощью [[Перечисление_точек_в_произвольном_прямоугольнике_за_n_*_log_%5E(d_-_1)_n_(range_tree)|техники поиска точек в d-мерном пространстве]]. Также читателю предлагается придумать и более эффективные и простые способы решить эту подзадачу.&lt;br /&gt;
&lt;br /&gt;
Оценим итоговую асимптотику: &amp;lt;tex&amp;gt;T(n) = k * T(n / k) + O(n \cdot log(n))&amp;lt;/tex&amp;gt;. Решая это рекурентное соотношение, получим &amp;lt;math&amp;gt;O(n \cdot log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь докажем лемму о существовании центроида и опишем алгоритм его эффективного поиска.&lt;br /&gt;
&lt;br /&gt;
=== Лемма о существовании центроида и алгоритм его нахождения. ===&lt;br /&gt;
{{Лемма|id=l1&lt;br /&gt;
|statement=&lt;br /&gt;
В любом дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; существует центроид.&lt;br /&gt;
|proof=&lt;br /&gt;
Рассмотрим корень дерева &amp;lt;math&amp;gt;(r)&amp;lt;/math&amp;gt;. Положим изначально &amp;lt;math&amp;gt;v = r&amp;lt;/math&amp;gt;. Изначально &amp;lt;math&amp;gt;|subtree(v)| = n&amp;lt;/math&amp;gt;. Среди всех детей &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; выберем вершину &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; с максимальным размером поддерева. Если &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид, то положим &amp;lt;math&amp;gt;v = u&amp;lt;/math&amp;gt; и продолжим выбор нового u, иначе {{---}} остановимся. Докажем, что мы в какой-то момент остановимся. Пусть в призвольный момент времени &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид и размер её наддерева меньше &amp;lt;math&amp;gt;\dfrac{n}{2}&amp;lt;/math&amp;gt;, значит максимальное поддерево имеет размер больше чем &amp;lt;math&amp;gt;\dfrac{n}{2}&amp;lt;/math&amp;gt;, т.е. &amp;lt;math&amp;gt;|subtree(u)| &amp;gt; \dfrac{n}{2}&amp;lt;/math&amp;gt;, а значит размер &amp;quot;наддерева&amp;quot; вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; равен &amp;lt;tex&amp;gt;n - |subtree(u)| &amp;lt; \dfrac{n}{2}&amp;lt;/tex&amp;gt;. При этом теперь размер любого поддерева, на которое распадется дерево t при удалении вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; не превосходит &amp;lt;math&amp;gt;|subtree(u)| - 1&amp;lt;/math&amp;gt;, т.к. наддерево имеет размер меньше, чем поддерево &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, а любое поддерево вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; имеет хотя бы на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; вершину меньше (сама вершина &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;). По индукции получаем, что в любой момент времени размер наддерева вершины v меньше &amp;lt;math&amp;gt;\dfrac{n}{2}&amp;lt;/math&amp;gt;, значит мы будем спускаться только вниз по дереву &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, и при переходе к вершине &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} сыну &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; размер максимального поддерева уменьшится как минимум на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Значит не более чем за &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; шагов наши действия прекратятся и мы окажемся в центроиде дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
Итак, мы конструктивно доказали существование центроида и привели линейный относительно размера дерева алгоритм его нахождения. &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Реализация ===&lt;br /&gt;
&lt;br /&gt;
Поиск центроида в дереве:&lt;br /&gt;
&lt;br /&gt;
  '''int''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int[]''' sz)&lt;br /&gt;
     max_subtree = -1&lt;br /&gt;
     '''for''' u : children(v)&lt;br /&gt;
          '''if''' sz[u] &amp;gt; sz[v] / 2 and sz[u] &amp;gt; sz[max_subtree] &amp;lt;font color=green&amp;gt;// считаем sz[-1] = 0 &amp;lt;/font &amp;gt;&lt;br /&gt;
               max_subtree = u&lt;br /&gt;
     '''if''' max_subtree == -1&lt;br /&gt;
          return v&lt;br /&gt;
     '''else''' &lt;br /&gt;
          '''return''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz)&lt;br /&gt;
&lt;br /&gt;
Шаблон решения произвольной задачи на статическую центроидную декомпозицию:&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int[]''' sz)&lt;br /&gt;
     c = &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz) &amp;lt;font color=green&amp;gt;// находим c {{---}} центроид поддерева вершины v &amp;lt;/font &amp;gt;&lt;br /&gt;
     ch = children[c]&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{deleteEdges}&amp;lt;/tex&amp;gt;(c, children) &amp;lt;font color=green&amp;gt;// удаляем все ребра между вершиной с и детьми, чтобы мы не смогли из детей попасть в с. &lt;br /&gt;
         Это полезно делать, если решение подзадачи для поддерева предполагает проход dfs &amp;lt;/font &amp;gt;&lt;br /&gt;
     '''for''' c2 : ch[c]&lt;br /&gt;
          &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;(children, c2, sz)&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{mergeSolution}&amp;lt;/tex&amp;gt;(children, c2) &amp;lt;font color=green&amp;gt;// решаем для текущего поддерева &amp;lt;/font &amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Динамическая центроидная декомпозиция (дерево центроидной декомпозиции) ==&lt;br /&gt;
Теперь вернемся ко второй задаче введения. Для ее решения мы пользовались динамичекой версией «разделяй и властвуй» {{---}} деревом отрезков. В предыдущем пункте мы определили статический аналог «разделяй и властвуй» для дерева. Теперь обобщим этот метод для динамических задач.&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Деревом центроидов (или центроидной декомпозицией)''' дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; называют дерево &amp;lt;math&amp;gt;T(t)&amp;lt;/math&amp;gt;, построенное на вершинах дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; следующим образом:&lt;br /&gt;
* Пусть вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} центроид дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Объявим его корнем нового дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пусть при удалении вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; из дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; оно распалось на поддеревья &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, тогда детьми вершины c объявим &amp;lt;tex&amp;gt;T(t_1), T(t_t),\dots, T(t_k)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
=== Свойства центроидной декомпозиции ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
'''Свойства центроидной декомпозиции''':&lt;br /&gt;
# Глубина дерева центроидов не превосходит &amp;lt;tex&amp;gt;log(n)&amp;lt;/tex&amp;gt;. &lt;br /&gt;
# Каждая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; является центроидом одного из поддеревьев дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждая вершина дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; поддеревьям дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (еще говорят, что вершина принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; центроидам дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, или что эти центроиды содержат вершину)&lt;br /&gt;
# Для любых вершин &amp;lt;tex&amp;gt;u, v \in T (u \neq v)&amp;lt;/tex&amp;gt; верно ровно одно из следующих трех утверждений: &lt;br /&gt;
#*&amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;&lt;br /&gt;
#* &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;&lt;br /&gt;
#* &amp;lt;tex&amp;gt;T(u) \cap T(v) = \emptyset &amp;lt;/tex&amp;gt;&lt;br /&gt;
# Простой путь между любой парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; содержит центроид &amp;lt;tex&amp;gt;c \in T(t)&amp;lt;/tex&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
# Действительно, т.к. размер поддерева &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; не превосходит &amp;lt;tex&amp;gt;\dfrac{|subtree(c)|}{2}&amp;lt;/tex&amp;gt;, то при спуске в каждую следующую вершину на пути к любому листу в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; размер поддерева вершины, в которой мы сейчас находимся, уменьшается как минимум на &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;. Значит длина всего пути до листа не превосходит &amp;lt;math&amp;gt;log(n)&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
# Второе свойство очевидно из построения дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, т.к. если вершина принадлежит дереву центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то она является центроидом, а из построения &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы знаем, что каждая вершина исходного дерева принадлежит и дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Третье свойство {{---}} прямое следствие первых двух, т.к. вершина принадлежит любому центроиду &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; т.и т.т., когда c {{---}} отец вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов. Т.к. вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; точно принадлежит дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (свойство 2), то она лежит на каком-то пути в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, причем все ее родители (центроиды) ее содержат. А по свойству 1 длина любого вертикального (и даже простого) пути есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
# Четвертое свойство очевидно из того, что &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; {{---}} дерево. Т.к. &amp;lt;math&amp;gt;T(u)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;T(v)&amp;lt;/math&amp;gt; {{---}} поддеревья различных вершин дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то либо они не пересекаются, либо &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;, либо &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Для доказательства последнего свойства выберем в качестве вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;lca(u, v)&amp;lt;/math&amp;gt; в дереве центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Покажем, что так выбранная вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удовлетворяет заявленным свойствам. То, что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt; {{---}} очевидно по определению &amp;lt;math&amp;gt;lca&amp;lt;/math&amp;gt;, т.к. каждый предок любой вершины в дереве центроидов содержит эту вершину. Теперь докажем, что &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежит на пути между парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt;. Т.к. &amp;lt;math&amp;gt;c = lca(u, v)&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то из &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; нет ребра в такого сына, который содержит одновременно &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в своем поддереве (в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;), значит после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; разделится на несколько поддеревьев, таких что вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; окажутся в разных компонентах связности. А значит найдется такое ребро &amp;lt;math&amp;gt;(c, x)&amp;lt;/math&amp;gt;, которое принадлежало пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, но после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удалилось. Это доказывает то, что вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежала на пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
С помощью описанных свойств дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы можем решить задачу 2 для дерева:&lt;br /&gt;
&lt;br /&gt;
=== Пример решения задачи с помощью центроидной декомпозиции === &lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин. В каждый момент времени любая вершина дерева может быть либо помечена, либо нет. Изначально помечена только вершина номер &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Дан список из &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; запросов: &lt;br /&gt;
* Вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; пометили.&lt;br /&gt;
* С вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; сняли пометку.&lt;br /&gt;
* Найти номер ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины.&lt;br /&gt;
}}&lt;br /&gt;
(Задача C со сборов СПБ к РОИ-2016&amp;lt;ref&amp;gt;[http://neerc.ifmo.ru/school/camp-2016/problems/20160324a1.pdf  Сайт с задачами Санкт-Петербургских сборов к РОИ 2016]&amp;lt;/ref&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
'''Решение:'''&lt;br /&gt;
&lt;br /&gt;
Построим центроидную декомпозицию &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Изначально посчитаем для каждого центроида, содержащего вершину &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt; посчитаем расстояние до вершины &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Для этого воспользуемся [[Метод_двоичного_подъема|методом двоичных подъемов для поиска lca пары вершин в дереве]], а также тем фактом, что если глубина &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве определена как расстояние от корня до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то длина пути между парой вершин &amp;lt;math&amp;gt;(u, v)&amp;lt;/math&amp;gt; есть &amp;lt;tex&amp;gt;len(u, v) = h(u) + h(v) - 2 \cdot h(lca(u, v))&amp;lt;/tex&amp;gt;. Если изначально предпосчитать проходом [[Обход_в_глубину,_цвета_вершин| dfs]] величины &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, то ответ на запрос &amp;lt;math&amp;gt;len(u, v)&amp;lt;/math&amp;gt; можно делать за время &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с &amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt;доп. памятью. Также можно воспользоваться техникой [[Сведение_задачи_LCA_к_задаче_RMQ|сведения задачи LCA к RMQ]] и решить с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; времени на запрос. Теперь научимся отвечать на запросы. Из последнего свойства центроидной декомпозиции видно, что если &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} искомая ближайшая помеченная вершина к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то путь между ними содержит центроид &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;, причем &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок одновременно вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве&amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Поэтому заведем [[Красно-черное_дерево|двоичное дерево поиска]] для каждого центроида &amp;lt;math&amp;gt;c \in T&amp;lt;/math&amp;gt;. В этой структуре для каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; будем хранить пары &amp;lt;math&amp;gt;(len(c, u), u)&amp;lt;/math&amp;gt; для всех помеченных вершин &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в поддереве центроидов &amp;lt;math&amp;gt;T(c)&amp;lt;/math&amp;gt;. Когда приходит запрос пометить вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} добавим в структуру данных для всех предков &amp;lt;math&amp;gt;p_c&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; пары &amp;lt;math&amp;gt;(len(p_c, v), v)&amp;lt;/math&amp;gt;. Мы совершим &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; добавлений, затратив &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; действий на каждое. Запрос снятия пометки с вершины обрабатывается аналогичными удалениями. Запрос поиска ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины {{---}} это запрос поиска вершины u, такой что  величина &amp;lt;math&amp;gt;len(c, u) + len(c, v)&amp;lt;/math&amp;gt; минимальна, где &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов (по пятому свойству, нас интересуют именно такие &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;). Этот запрос занимает так же &amp;lt;tex&amp;gt;O(log^2(n))&amp;lt;/tex&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
Итак, мы научились решать задачу с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и временной сложностью &amp;lt;math&amp;gt;O(log^2(n))&amp;lt;/math&amp;gt; на запрос любого типа с предварительным предпосчетом за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Варианты хранения центроидной декомпозиции ==&lt;br /&gt;
Для хранения дерева центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; есть &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; подхода, имеющих свои преимущества и недостатки:&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева запомним величину &amp;lt;math&amp;gt;p_v&amp;lt;/math&amp;gt; {{---}} номер предка вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Этот подход наиболее экономный по памяти (&amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;), но уступает в скорости и функциональности.&lt;br /&gt;
&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева будем хранить весь массив предков &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt; в дереве центроидов.&lt;br /&gt;
&lt;br /&gt;
Этот подход уступает в количестве необходимой дополнительной памяти (&amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt; суммарно), но имеет ряд преимуществ:&lt;br /&gt;
&lt;br /&gt;
#При проходе по массиву предков фиксированной вершины будет выигрыш в скорости работы, т.к. весь массив будет лежать непрерывным блоком данных и следовательно будет закэширован&lt;br /&gt;
# На массиве предков можно строить различные структуры данных (такие как, например, дерево отрезков) для быстрого (в случае с деревом отрезков &amp;lt;math&amp;gt;O(log(log(n)))&amp;lt;/math&amp;gt; на запрос) поиска предка с необходимыми свойствами. Так, например, в описанной выше задаче про помеченные вершины наибольшего общего предка можно искать методом двоичных подъемов за &amp;lt;math&amp;gt;O(log(log(n))&amp;lt;/math&amp;gt; на запрос, т.к. размер массива предков есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; (по свойству 1 центроидной декомпозиции). Используя эту оптимизацию можно получить время &amp;lt;math&amp;gt;O(log(n)) + O(log(log(n)) = O(log(n))&amp;lt;/math&amp;gt; на запрос нахождения ближайшей помеченной вершины. Чтобы добиться улучшенной асимптотики для запросов изменения можно хранить дерево отрезков на каждом из путей &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt;, в каждой вершине которого хранить двоичное дерево поиска и поддерживать отложенные операции. Тогда ответ на эти запросы будет занимать &amp;lt;math&amp;gt;O(log(n) \cdot log(log(n))&amp;lt;/math&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
&lt;br /&gt;
*[[:Дерево_отрезков._Построение|Дерево отрезков]]&lt;br /&gt;
*[[:Heavy-light_декомпозиция|Heavy-light декомпозиция]]&lt;br /&gt;
*[[:Метод_двоичного_подъема| Метод двоичного подъема]]&lt;br /&gt;
&lt;br /&gt;
== Примечания == &lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы и структуры данных]]&lt;/div&gt;</summary>
		<author><name>92.255.113.52</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61644</id>
		<title>Centroid decomposition</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61644"/>
				<updated>2017-06-14T22:32:30Z</updated>
		
		<summary type="html">&lt;p&gt;92.255.113.52: /* Реализация */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Centroid decomposition (рус. центроидная декомпозиция) {{---}} это структура данных, позволяющая отвечать на запросы на дереве. Чаще всего это запросы, связанные с нахождением функции на вершинах, связанных неравенством на расстояние между ними в дереве. Также иногда применяется для запросов на путях в дереве.&lt;br /&gt;
&lt;br /&gt;
== Введение ==&lt;br /&gt;
Рассмотрим &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; задачи на обычном массиве (в дальнейшем мы будем их обобщать на случай дерева):&lt;br /&gt;
&lt;br /&gt;
Задача &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть массив &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; положительных целых чисел из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов. Также дано число &amp;lt;tex&amp;gt;W \geqslant 0&amp;lt;/tex&amp;gt; и число &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; индексов массива, таких что &amp;lt;tex&amp;gt;|j - i| \leqslant l &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\sum_{k=i}^{j} a_k \leqslant W&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
Задача &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть прямая дорога, на которой расположены &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; городов. В некоторых городах есть госпитали, которые могут принимать больных. Поступают запросы вида:&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;, в котором находится больной и требуется найти такой город &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; может принимать больных и &amp;lt;tex&amp;gt;|u - v|&amp;lt;/tex&amp;gt; минимально возможное.&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что больше он не будет принимать больных&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что теперь он может принимать больных&lt;br /&gt;
}}&lt;br /&gt;
Для начала решим обе задачи. &lt;br /&gt;
Первая задача решается методом [[Сортировка_слиянием|«разделяй и властвуй»]] {{---}} давайте разделим массив &amp;lt;tex&amp;gt;a[0 \dots n-1]&amp;lt;/tex&amp;gt; на 2 массива &amp;lt;tex&amp;gt;a[0\dots\dfrac{n}{2} - 1]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;a[\dfrac{n}{2} \dots n-1]&amp;lt;/tex&amp;gt; и рекурсивно решим задачу для каждого из них. Осталось научиться находить количество искомых пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt;, таких что &amp;lt;tex&amp;gt;i &amp;lt; \dfrac{n}{2}, j \geqslant \dfrac{n}{2}&amp;lt;/tex&amp;gt;. Для этого воспользуемся другой известной техникой {{---}} методом двух указателей. Посчитаем массив префиксных сумм для правой половины &amp;lt;tex&amp;gt;pref[i] = \sum_{j=\frac{n}{2}}^{i} a_j&amp;lt;/tex&amp;gt; и суффиксных (&amp;lt;tex&amp;gt;suf[i] = \sum_{j=i}^{\frac{n}{2} + 1} a_j&amp;lt;/tex&amp;gt;) {{---}} для левой. Заведем два указателя (&amp;lt;tex&amp;gt;p_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;p_2&amp;lt;/tex&amp;gt;). Изначально установим &amp;lt;tex&amp;gt;p_1 = \dfrac{n}{2} - l + 1, p_2 = \dfrac{n}{2}&amp;lt;/tex&amp;gt;. Пока &amp;lt;tex&amp;gt;p_2 - 1&amp;gt; \dfrac{n}{2}&amp;lt;/tex&amp;gt; и&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;pref[p_2] + suf[p_1] &amp;gt; W &amp;lt;/tex&amp;gt; будем уменьшать &amp;lt;math&amp;gt;p_2&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Если после этого &amp;lt;math&amp;gt;pref[p_2] + suf[p_1] \leqslant W&amp;lt;/math&amp;gt;, то к ответу прибавим &amp;lt;math&amp;gt;(p_2 - \dfrac{n}{2} + 1) * (\dfrac{n}{2} - p_1)&amp;lt;/math&amp;gt;, посго, увеличим &amp;lt;math&amp;gt;p_1&amp;lt;/math&amp;gt; на &amp;lt;math/math&amp;gt;. Так будем делать, пока &amp;lt;math&amp;gt;p_1 &amp;lt; \dfrac{n}{2}&amp;lt;/math&amp;gt;. В конце сложим текущий ответ и ответы для половин массива {{---}} получим ответ на задачу. Асимптотика такого алгоритма: &amp;lt;tex&amp;gt;T(n) = 2 * T(n / 2) + O(n) = O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вторая задача имеет запросы на изменение и поэтому надо применить динамическую версию «разделяй и властвуй» {{---}} [[Дерево_отрезков._Построение|дерево отрезков]]. Построим дерево отрезков,&lt;br /&gt;
поддерживающее 2 вида запросов: присвоение в точке и минимум на отрезке. Изначально сделаем так, чтобы дереву отрезков соответствовал массив &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;b_i = 0&amp;lt;/tex&amp;gt;, если в i-м городе принимает госпиталь и &amp;lt;tex&amp;gt;b_i = 1&amp;lt;/tex&amp;gt; иначе. Когда в каком-то городе открывается/закрывается госпиталь {{---}} делаем запрос на изменение в дереве отрезков. Когда требуется узнать ближайщий госпиталь к &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-му городу, можно воспользоваться одной из следующих идей: &lt;br /&gt;
а) (&amp;lt;tex&amp;gt;O(n \cdot log^2(n)&amp;lt;/tex&amp;gt;) Бинарным поиском ищем ближайший слева и ближайший справа к i-му городу госпиталь (такой город &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, что &amp;lt;tex&amp;gt;\min\limits_{k=i..j}b_k= 1&amp;lt;/tex&amp;gt;). Для этого внутри бинарного поиска каждый раз делаем запрос на поиск минимума в дереве отрезков.  &lt;br /&gt;
б) (&amp;lt;tex&amp;gt;O(n \cdot log(n)&amp;lt;/tex&amp;gt;) Будем одним спуском/подъемом по дереву определять, куда нам нужно идти (в левое или правое поддерево), тем самым делая одновоременно и бинарный поиск, и спуск/подъем по дереву.&lt;br /&gt;
&lt;br /&gt;
== Статическая центроидная декомпозиция ==&lt;br /&gt;
Перейдем к обобщению поставленных задач на случай дерева. Начнем, как и полагается, с первой:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть взвешенное дерево &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин, в каждой вершине которого написаны положительные целые числа. Также по-прежнему даны числа &amp;lt;tex&amp;gt;W \geqslant 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; вершин дерева, таких что расстояние между ними не превосходит &amp;lt;math&amp;gt;l&amp;lt;/math&amp;gt; по числу ребер и не превосходит &amp;lt;math&amp;gt;W&amp;lt;/math&amp;gt; по сумме весов.&lt;br /&gt;
}}&lt;br /&gt;
(Задача D со сборов СПБ к РОИ-2016&amp;lt;ref&amp;gt;[http://neerc.ifmo.ru/school/camp-2016/problems/20160324a1.pdf  Сайт с задачами Санкт-Петербургских сборов к РОИ 2016]&amp;lt;/ref&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
Для решения новой задачи применим ту же идею, что и была до этого {{---}} разделяй и властвуй. Для этого нам потребуется следующий объект:&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|id=191213&lt;br /&gt;
|definition =&lt;br /&gt;
'''Центроидом дерева''' (англ. ''centroid'') называется такая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, после удаления которой дерево разбивается на несколько (&amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;) поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, таких что для каждого &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;: &amp;lt;tex&amp;gt;|t_i| \leqslant \dfrac{n}{2}&amp;lt;/tex&amp;gt;, т.е. размер каждого поддерева не превосходит половины размера исходного дерева.&lt;br /&gt;
}}&lt;br /&gt;
Итак, в случае дерева идея разделяй-и-властвуй из предыдущего пункта будет формулироваться так: найдем центроид. Предположим, что мы сумели найти центроид за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; {{---}} размер дерева. Тогда, как и в упрощенной версии задачи {{---}} рекурсивно найдем ответ для всех поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, после чего попытаемся найти недостающие пары вершин, находящиехя в разных поддеревьях и удовлетворяющих вопросу задачи. Для этого будем отвечать на следующие запросы: пусть мы сейчас считаем все пары, где первая из вершин находится в поддереве &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и мы в некоторой структуре данных &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; храним все вершины остальных деревьев (каждую вершину задаем парой &amp;lt;math&amp;gt;(depth(v), length(v))&amp;lt;/math&amp;gt; {{---}} глубина вершины и длина пути до нее из корня поддерева), расстояние до которых от корня их поддерева не превышает &amp;lt;math&amp;gt;min(l, n)&amp;lt;/math&amp;gt;. Тогда просто пройдемся по всем вершинам &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; поддерева &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и прибавим к ответу число вершин в структуре &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt;, таких, что &amp;lt;tex&amp;gt;depth(u) \leqslant l - depth(v)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;length(u) \leqslant l - length(v)&amp;lt;/tex&amp;gt;. Это двумерные запросы, на которые можно отвечать за &amp;lt;math&amp;gt;O(log^2(n)&amp;lt;/math&amp;gt; с помощью [[Многомерное_дерево_отрезков|2d-дерева отрезков]], либо за &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с помощью [[Перечисление_точек_в_произвольном_прямоугольнике_за_n_*_log_%5E(d_-_1)_n_(range_tree)|техники поиска точек в d-мерном пространстве]]. Также читателю предлагается придумать и более эффективные и простые способы решить эту подзадачу.&lt;br /&gt;
&lt;br /&gt;
Оценим итоговую асимптотику: &amp;lt;tex&amp;gt;T(n) = k * T(n / k) + O(n \cdot log(n))&amp;lt;/tex&amp;gt;. Решая это рекурентное соотношение, получим &amp;lt;math&amp;gt;O(n \cdot log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь докажем лемму о существовании центроида и опишем алгоритм его эффективного поиска.&lt;br /&gt;
&lt;br /&gt;
=== Лемма о существовании центроида и алгоритм его нахождения. ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
В любом дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; существует центроид.&lt;br /&gt;
|proof=&lt;br /&gt;
Рассмотрим корень дерева &amp;lt;math&amp;gt;(r)&amp;lt;/math&amp;gt;. Положим изначально &amp;lt;math&amp;gt;v = r&amp;lt;/math&amp;gt;. Изначально &amp;lt;math&amp;gt;|subtree(v)| = n&amp;lt;/math&amp;gt;. Среди всех детей &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; выберем вершину &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; с максимальным размером поддерева. Если &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид, то положим &amp;lt;math&amp;gt;v = u&amp;lt;/math&amp;gt; и продолжим выбор нового u, иначе {{---}} остановимся. Докажем, что мы в какой-то момент остановимся. Пусть в призвольный момент времени &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид и размер её наддерева меньше &amp;lt;math&amp;gt;\dfrac{n}{2}&amp;lt;/math&amp;gt;, значит максимальное поддерево имеет размер больше чем &amp;lt;math&amp;gt;\dfrac{n}{2}&amp;lt;/math&amp;gt;, т.е. &amp;lt;math&amp;gt;|subtree(u)| &amp;gt; \dfrac{n}{2}&amp;lt;/math&amp;gt;, а значит размер &amp;quot;наддерева&amp;quot; вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; равен &amp;lt;tex&amp;gt;n - |subtree(u)| &amp;lt; \dfrac{n}{2}&amp;lt;/tex&amp;gt;. При этом теперь размер любого поддерева, на которое распадется дерево t при удалении вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; не превосходит &amp;lt;math&amp;gt;|subtree(u)| - 1&amp;lt;/math&amp;gt;, т.к. наддерево имеет размер меньше, чем поддерево &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, а любое поддерево вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; имеет хотя бы на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; вершину меньше (сама вершина &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;). По индукции получаем, что в любой момент времени размер наддерева вершины v меньше &amp;lt;math&amp;gt;\dfrac{n}{2}&amp;lt;/math&amp;gt;, значит мы будем спускаться только вниз по дереву &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, и при переходе к вершине &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} сыну &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; размер максимального поддерева уменьшится как минимум на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Значит не более чем за &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; шагов наши действия прекратятся и мы окажемся в центроиде дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
Итак, мы конструктивно доказали существование центроида и привели линейный относительно размера дерева алгоритм его нахождения. &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Реализация ===&lt;br /&gt;
&lt;br /&gt;
Поиск центроида в дереве:&lt;br /&gt;
&lt;br /&gt;
  '''int''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int[]''' sz)&lt;br /&gt;
     max_subtree = -1&lt;br /&gt;
     '''for''' u : children(v)&lt;br /&gt;
          '''if''' sz[u] &amp;gt; sz[v] / 2 and sz[u] &amp;gt; sz[max_subtree] &amp;lt;font color=green&amp;gt;// считаем sz[-1] = 0 &amp;lt;/font &amp;gt;&lt;br /&gt;
               max_subtree = u&lt;br /&gt;
     '''if''' max_subtree == -1&lt;br /&gt;
          return v&lt;br /&gt;
     '''else''' &lt;br /&gt;
          '''return''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz)&lt;br /&gt;
&lt;br /&gt;
Шаблон решения произвольной задачи на статическую центроидную декомпозицию:&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int[]''' sz)&lt;br /&gt;
     c = &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz) &amp;lt;font color=green&amp;gt;// находим c {{---}} центроид поддерева вершины v &amp;lt;/font &amp;gt;&lt;br /&gt;
     ch = children[c]&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{deleteEdges}&amp;lt;/tex&amp;gt;(c, children) &amp;lt;font color=green&amp;gt;// удаляем все ребра между вершиной с и детьми, чтобы мы не смогли из детей попасть в с. &lt;br /&gt;
         Это полезно делать, если решение подзадачи для поддерева предполагает проход dfs &amp;lt;/font &amp;gt;&lt;br /&gt;
     '''for''' c2 : ch[c]&lt;br /&gt;
          &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;(children, c2, sz)&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{mergeSolution}&amp;lt;/tex&amp;gt;(children, c2) &amp;lt;font color=green&amp;gt;// решаем для текущего поддерева &amp;lt;/font &amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Динамическая центроидная декомпозиция (дерево центроидной декомпозиции) ==&lt;br /&gt;
Теперь вернемся ко второй задаче введения. Для ее решения мы пользовались динамичекой версией «разделяй и властвуй» {{---}} деревом отрезков. В предыдущем пункте мы определили статический аналог «разделяй и властвуй» для дерева. Теперь обобщим этот метод для динамических задач.&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Деревом центроидов (или центроидной декомпозицией)''' дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; называют дерево &amp;lt;math&amp;gt;T(t)&amp;lt;/math&amp;gt;, построенное на вершинах дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; следующим образом:&lt;br /&gt;
* Пусть вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} центроид дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Объявим его корнем нового дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пусть при удалении вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; из дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; оно распалось на поддеревья &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, тогда детьми вершины c объявим &amp;lt;tex&amp;gt;T(t_1), T(t_t),\dots, T(t_k)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
=== Свойства центроидной декомпозиции ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
'''Свойства центроидной декомпозиции''':&lt;br /&gt;
# Глубина дерева центроидов не превосходит &amp;lt;tex&amp;gt;log(n)&amp;lt;/tex&amp;gt;. &lt;br /&gt;
# Каждая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; является центроидом одного из поддеревьев дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждая вершина дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; поддеревьям дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (еще говорят, что вершина принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; центроидам дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, или что эти центроиды содержат вершину)&lt;br /&gt;
# Для любых вершин &amp;lt;tex&amp;gt;u, v \in T (u \neq v)&amp;lt;/tex&amp;gt; верно ровно одно из следующих трех утверждений: &lt;br /&gt;
#*&amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;&lt;br /&gt;
#* &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;&lt;br /&gt;
#* &amp;lt;tex&amp;gt;T(u) \cap T(v) = \emptyset &amp;lt;/tex&amp;gt;&lt;br /&gt;
# Простой путь между любой парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; содержит центроид &amp;lt;tex&amp;gt;c \in T(t)&amp;lt;/tex&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
# Действительно, т.к. размер поддерева &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; не превосходит &amp;lt;tex&amp;gt;\dfrac{|subtree(c)|}{2}&amp;lt;/tex&amp;gt;, то при спуске в каждую следующую вершину на пути к любому листу в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; размер поддерева вершины, в которой мы сейчас находимся, уменьшается как минимум на &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;. Значит длина всего пути до листа не превосходит &amp;lt;math&amp;gt;log(n)&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
# Второе свойство очевидно из построения дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, т.к. если вершина принадлежит дереву центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то она является центроидом, а из построения &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы знаем, что каждая вершина исходного дерева принадлежит и дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Третье свойство {{---}} прямое следствие первых двух, т.к. вершина принадлежит любому центроиду &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; т.и т.т., когда c {{---}} отец вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов. Т.к. вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; точно принадлежит дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (свойство 2), то она лежит на каком-то пути в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, причем все ее родители (центроиды) ее содержат. А по свойству 1 длина любого вертикального (и даже простого) пути есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
# Четвертое свойство очевидно из того, что &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; {{---}} дерево. Т.к. &amp;lt;math&amp;gt;T(u)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;T(v)&amp;lt;/math&amp;gt; {{---}} поддеревья различных вершин дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то либо они не пересекаются, либо &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;, либо &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Для доказательства последнего свойства выберем в качестве вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;lca(u, v)&amp;lt;/math&amp;gt; в дереве центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Покажем, что так выбранная вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удовлетворяет заявленным свойствам. То, что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt; {{---}} очевидно по определению &amp;lt;math&amp;gt;lca&amp;lt;/math&amp;gt;, т.к. каждый предок любой вершины в дереве центроидов содержит эту вершину. Теперь докажем, что &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежит на пути между парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt;. Т.к. &amp;lt;math&amp;gt;c = lca(u, v)&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то из &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; нет ребра в такого сына, который содержит одновременно &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в своем поддереве (в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;), значит после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; разделится на несколько поддеревьев, таких что вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; окажутся в разных компонентах связности. А значит найдется такое ребро &amp;lt;math&amp;gt;(c, x)&amp;lt;/math&amp;gt;, которое принадлежало пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, но после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удалилось. Это доказывает то, что вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежала на пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
С помощью описанных свойств дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы можем решить задачу 2 для дерева:&lt;br /&gt;
&lt;br /&gt;
=== Пример решения задачи с помощью центроидной декомпозиции === &lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин. В каждый момент времени любая вершина дерева может быть либо помечена, либо нет. Изначально помечена только вершина номер &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Дан список из &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; запросов: &lt;br /&gt;
* Вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; пометили.&lt;br /&gt;
* С вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; сняли пометку.&lt;br /&gt;
* Найти номер ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины.&lt;br /&gt;
}}&lt;br /&gt;
(Задача C со сборов СПБ к РОИ-2016&amp;lt;ref&amp;gt;[http://neerc.ifmo.ru/school/camp-2016/problems/20160324a1.pdf  Сайт с задачами Санкт-Петербургских сборов к РОИ 2016]&amp;lt;/ref&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
'''Решение:'''&lt;br /&gt;
&lt;br /&gt;
Построим центроидную декомпозицию &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Изначально посчитаем для каждого центроида, содержащего вершину &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt; посчитаем расстояние до вершины &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Для этого воспользуемся [[Метод_двоичного_подъема|методом двоичных подъемов для поиска lca пары вершин в дереве]], а также тем фактом, что если глубина &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве определена как расстояние от корня до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то длина пути между парой вершин &amp;lt;math&amp;gt;(u, v)&amp;lt;/math&amp;gt; есть &amp;lt;tex&amp;gt;len(u, v) = h(u) + h(v) - 2 \cdot h(lca(u, v))&amp;lt;/tex&amp;gt;. Если изначально предпосчитать проходом [[Обход_в_глубину,_цвета_вершин| dfs]] величины &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, то ответ на запрос &amp;lt;math&amp;gt;len(u, v)&amp;lt;/math&amp;gt; можно делать за время &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с &amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt;доп. памятью. Также можно воспользоваться техникой [[Сведение_задачи_LCA_к_задаче_RMQ|сведения задачи LCA к RMQ]] и решить с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; времени на запрос. Теперь научимся отвечать на запросы. Из последнего свойства центроидной декомпозиции видно, что если &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} искомая ближайшая помеченная вершина к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то путь между ними содержит центроид &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;, причем &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок одновременно вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве&amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Поэтому заведем [[Красно-черное_дерево|двоичное дерево поиска]] для каждого центроида &amp;lt;math&amp;gt;c \in T&amp;lt;/math&amp;gt;. В этой структуре для каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; будем хранить пары &amp;lt;math&amp;gt;(len(c, u), u)&amp;lt;/math&amp;gt; для всех помеченных вершин &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в поддереве центроидов &amp;lt;math&amp;gt;T(c)&amp;lt;/math&amp;gt;. Когда приходит запрос пометить вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} добавим в структуру данных для всех предков &amp;lt;math&amp;gt;p_c&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; пары &amp;lt;math&amp;gt;(len(p_c, v), v)&amp;lt;/math&amp;gt;. Мы совершим &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; добавлений, затратив &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; действий на каждое. Запрос снятия пометки с вершины обрабатывается аналогичными удалениями. Запрос поиска ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины {{---}} это запрос поиска вершины u, такой что  величина &amp;lt;math&amp;gt;len(c, u) + len(c, v)&amp;lt;/math&amp;gt; минимальна, где &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов (по пятому свойству, нас интересуют именно такие &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;). Этот запрос занимает так же &amp;lt;tex&amp;gt;O(log^2(n))&amp;lt;/tex&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
Итак, мы научились решать задачу с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и временной сложностью &amp;lt;math&amp;gt;O(log^2(n))&amp;lt;/math&amp;gt; на запрос любого типа с предварительным предпосчетом за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Варианты хранения центроидной декомпозиции ==&lt;br /&gt;
Для хранения дерева центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; есть &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; подхода, имеющих свои преимущества и недостатки:&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева запомним величину &amp;lt;math&amp;gt;p_v&amp;lt;/math&amp;gt; {{---}} номер предка вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Этот подход наиболее экономный по памяти (&amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;), но уступает в скорости и функциональности.&lt;br /&gt;
&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева будем хранить весь массив предков &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt; в дереве центроидов.&lt;br /&gt;
&lt;br /&gt;
Этот подход уступает в количестве необходимой дополнительной памяти (&amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt; суммарно), но имеет ряд преимуществ:&lt;br /&gt;
&lt;br /&gt;
#При проходе по массиву предков фиксированной вершины будет выигрыш в скорости работы, т.к. весь массив будет лежать непрерывным блоком данных и следовательно будет закэширован&lt;br /&gt;
# На массиве предков можно строить различные структуры данных (такие как, например, дерево отрезков) для быстрого (в случае с деревом отрезков &amp;lt;math&amp;gt;O(log(log(n)))&amp;lt;/math&amp;gt; на запрос) поиска предка с необходимыми свойствами. Так, например, в описанной выше задаче про помеченные вершины наибольшего общего предка можно искать методом двоичных подъемов за &amp;lt;math&amp;gt;O(log(log(n))&amp;lt;/math&amp;gt; на запрос, т.к. размер массива предков есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; (по свойству 1 центроидной декомпозиции). Используя эту оптимизацию можно получить время &amp;lt;math&amp;gt;O(log(n)) + O(log(log(n)) = O(log(n))&amp;lt;/math&amp;gt; на запрос нахождения ближайшей помеченной вершины. Чтобы добиться улучшенной асимптотики для запросов изменения можно хранить дерево отрезков на каждом из путей &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt;, в каждой вершине которого хранить двоичное дерево поиска и поддерживать отложенные операции. Тогда ответ на эти запросы будет занимать &amp;lt;math&amp;gt;O(log(n) \cdot log(log(n))&amp;lt;/math&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
&lt;br /&gt;
*[[:Дерево_отрезков._Построение|Дерево отрезков]]&lt;br /&gt;
*[[:Heavy-light_декомпозиция|Heavy-light декомпозиция]]&lt;br /&gt;
*[[:Метод_двоичного_подъема| Метод двоичного подъема]]&lt;br /&gt;
&lt;br /&gt;
== Примечания == &lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы и структуры данных]]&lt;/div&gt;</summary>
		<author><name>92.255.113.52</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61643</id>
		<title>Centroid decomposition</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61643"/>
				<updated>2017-06-14T22:30:28Z</updated>
		
		<summary type="html">&lt;p&gt;92.255.113.52: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Centroid decomposition (рус. центроидная декомпозиция) {{---}} это структура данных, позволяющая отвечать на запросы на дереве. Чаще всего это запросы, связанные с нахождением функции на вершинах, связанных неравенством на расстояние между ними в дереве. Также иногда применяется для запросов на путях в дереве.&lt;br /&gt;
&lt;br /&gt;
== Введение ==&lt;br /&gt;
Рассмотрим &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; задачи на обычном массиве (в дальнейшем мы будем их обобщать на случай дерева):&lt;br /&gt;
&lt;br /&gt;
Задача &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть массив &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; положительных целых чисел из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов. Также дано число &amp;lt;tex&amp;gt;W \geqslant 0&amp;lt;/tex&amp;gt; и число &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; индексов массива, таких что &amp;lt;tex&amp;gt;|j - i| \leqslant l &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\sum_{k=i}^{j} a_k \leqslant W&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
Задача &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть прямая дорога, на которой расположены &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; городов. В некоторых городах есть госпитали, которые могут принимать больных. Поступают запросы вида:&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;, в котором находится больной и требуется найти такой город &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; может принимать больных и &amp;lt;tex&amp;gt;|u - v|&amp;lt;/tex&amp;gt; минимально возможное.&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что больше он не будет принимать больных&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что теперь он может принимать больных&lt;br /&gt;
}}&lt;br /&gt;
Для начала решим обе задачи. &lt;br /&gt;
Первая задача решается методом [[Сортировка_слиянием|«разделяй и властвуй»]] {{---}} давайте разделим массив &amp;lt;tex&amp;gt;a[0 \dots n-1]&amp;lt;/tex&amp;gt; на 2 массива &amp;lt;tex&amp;gt;a[0\dots\dfrac{n}{2} - 1]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;a[\dfrac{n}{2} \dots n-1]&amp;lt;/tex&amp;gt; и рекурсивно решим задачу для каждого из них. Осталось научиться находить количество искомых пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt;, таких что &amp;lt;tex&amp;gt;i &amp;lt; \dfrac{n}{2}, j \geqslant \dfrac{n}{2}&amp;lt;/tex&amp;gt;. Для этого воспользуемся другой известной техникой {{---}} методом двух указателей. Посчитаем массив префиксных сумм для правой половины &amp;lt;tex&amp;gt;pref[i] = \sum_{j=\frac{n}{2}}^{i} a_j&amp;lt;/tex&amp;gt; и суффиксных (&amp;lt;tex&amp;gt;suf[i] = \sum_{j=i}^{\frac{n}{2} + 1} a_j&amp;lt;/tex&amp;gt;) {{---}} для левой. Заведем два указателя (&amp;lt;tex&amp;gt;p_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;p_2&amp;lt;/tex&amp;gt;). Изначально установим &amp;lt;tex&amp;gt;p_1 = \dfrac{n}{2} - l + 1, p_2 = \dfrac{n}{2}&amp;lt;/tex&amp;gt;. Пока &amp;lt;tex&amp;gt;p_2 - 1&amp;gt; \dfrac{n}{2}&amp;lt;/tex&amp;gt; и&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;pref[p_2] + suf[p_1] &amp;gt; W &amp;lt;/tex&amp;gt; будем уменьшать &amp;lt;math&amp;gt;p_2&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Если после этого &amp;lt;math&amp;gt;pref[p_2] + suf[p_1] \leqslant W&amp;lt;/math&amp;gt;, то к ответу прибавим &amp;lt;math&amp;gt;(p_2 - \dfrac{n}{2} + 1) * (\dfrac{n}{2} - p_1)&amp;lt;/math&amp;gt;, посго, увеличим &amp;lt;math&amp;gt;p_1&amp;lt;/math&amp;gt; на &amp;lt;math/math&amp;gt;. Так будем делать, пока &amp;lt;math&amp;gt;p_1 &amp;lt; \dfrac{n}{2}&amp;lt;/math&amp;gt;. В конце сложим текущий ответ и ответы для половин массива {{---}} получим ответ на задачу. Асимптотика такого алгоритма: &amp;lt;tex&amp;gt;T(n) = 2 * T(n / 2) + O(n) = O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вторая задача имеет запросы на изменение и поэтому надо применить динамическую версию «разделяй и властвуй» {{---}} [[Дерево_отрезков._Построение|дерево отрезков]]. Построим дерево отрезков,&lt;br /&gt;
поддерживающее 2 вида запросов: присвоение в точке и минимум на отрезке. Изначально сделаем так, чтобы дереву отрезков соответствовал массив &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;b_i = 0&amp;lt;/tex&amp;gt;, если в i-м городе принимает госпиталь и &amp;lt;tex&amp;gt;b_i = 1&amp;lt;/tex&amp;gt; иначе. Когда в каком-то городе открывается/закрывается госпиталь {{---}} делаем запрос на изменение в дереве отрезков. Когда требуется узнать ближайщий госпиталь к &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-му городу, можно воспользоваться одной из следующих идей: &lt;br /&gt;
а) (&amp;lt;tex&amp;gt;O(n \cdot log^2(n)&amp;lt;/tex&amp;gt;) Бинарным поиском ищем ближайший слева и ближайший справа к i-му городу госпиталь (такой город &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, что &amp;lt;tex&amp;gt;\min\limits_{k=i..j}b_k= 1&amp;lt;/tex&amp;gt;). Для этого внутри бинарного поиска каждый раз делаем запрос на поиск минимума в дереве отрезков.  &lt;br /&gt;
б) (&amp;lt;tex&amp;gt;O(n \cdot log(n)&amp;lt;/tex&amp;gt;) Будем одним спуском/подъемом по дереву определять, куда нам нужно идти (в левое или правое поддерево), тем самым делая одновоременно и бинарный поиск, и спуск/подъем по дереву.&lt;br /&gt;
&lt;br /&gt;
== Статическая центроидная декомпозиция ==&lt;br /&gt;
Перейдем к обобщению поставленных задач на случай дерева. Начнем, как и полагается, с первой:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть взвешенное дерево &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин, в каждой вершине которого написаны положительные целые числа. Также по-прежнему даны числа &amp;lt;tex&amp;gt;W \geqslant 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; вершин дерева, таких что расстояние между ними не превосходит &amp;lt;math&amp;gt;l&amp;lt;/math&amp;gt; по числу ребер и не превосходит &amp;lt;math&amp;gt;W&amp;lt;/math&amp;gt; по сумме весов.&lt;br /&gt;
}}&lt;br /&gt;
(Задача D со сборов СПБ к РОИ-2016&amp;lt;ref&amp;gt;[http://neerc.ifmo.ru/school/camp-2016/problems/20160324a1.pdf  Сайт с задачами Санкт-Петербургских сборов к РОИ 2016]&amp;lt;/ref&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
Для решения новой задачи применим ту же идею, что и была до этого {{---}} разделяй и властвуй. Для этого нам потребуется следующий объект:&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|id=191213&lt;br /&gt;
|definition =&lt;br /&gt;
'''Центроидом дерева''' (англ. ''centroid'') называется такая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, после удаления которой дерево разбивается на несколько (&amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;) поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, таких что для каждого &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;: &amp;lt;tex&amp;gt;|t_i| \leqslant \dfrac{n}{2}&amp;lt;/tex&amp;gt;, т.е. размер каждого поддерева не превосходит половины размера исходного дерева.&lt;br /&gt;
}}&lt;br /&gt;
Итак, в случае дерева идея разделяй-и-властвуй из предыдущего пункта будет формулироваться так: найдем центроид. Предположим, что мы сумели найти центроид за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; {{---}} размер дерева. Тогда, как и в упрощенной версии задачи {{---}} рекурсивно найдем ответ для всех поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, после чего попытаемся найти недостающие пары вершин, находящиехя в разных поддеревьях и удовлетворяющих вопросу задачи. Для этого будем отвечать на следующие запросы: пусть мы сейчас считаем все пары, где первая из вершин находится в поддереве &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и мы в некоторой структуре данных &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; храним все вершины остальных деревьев (каждую вершину задаем парой &amp;lt;math&amp;gt;(depth(v), length(v))&amp;lt;/math&amp;gt; {{---}} глубина вершины и длина пути до нее из корня поддерева), расстояние до которых от корня их поддерева не превышает &amp;lt;math&amp;gt;min(l, n)&amp;lt;/math&amp;gt;. Тогда просто пройдемся по всем вершинам &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; поддерева &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и прибавим к ответу число вершин в структуре &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt;, таких, что &amp;lt;tex&amp;gt;depth(u) \leqslant l - depth(v)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;length(u) \leqslant l - length(v)&amp;lt;/tex&amp;gt;. Это двумерные запросы, на которые можно отвечать за &amp;lt;math&amp;gt;O(log^2(n)&amp;lt;/math&amp;gt; с помощью [[Многомерное_дерево_отрезков|2d-дерева отрезков]], либо за &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с помощью [[Перечисление_точек_в_произвольном_прямоугольнике_за_n_*_log_%5E(d_-_1)_n_(range_tree)|техники поиска точек в d-мерном пространстве]]. Также читателю предлагается придумать и более эффективные и простые способы решить эту подзадачу.&lt;br /&gt;
&lt;br /&gt;
Оценим итоговую асимптотику: &amp;lt;tex&amp;gt;T(n) = k * T(n / k) + O(n \cdot log(n))&amp;lt;/tex&amp;gt;. Решая это рекурентное соотношение, получим &amp;lt;math&amp;gt;O(n \cdot log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь докажем лемму о существовании центроида и опишем алгоритм его эффективного поиска.&lt;br /&gt;
&lt;br /&gt;
=== Лемма о существовании центроида и алгоритм его нахождения. ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
В любом дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; существует центроид.&lt;br /&gt;
|proof=&lt;br /&gt;
Рассмотрим корень дерева &amp;lt;math&amp;gt;(r)&amp;lt;/math&amp;gt;. Положим изначально &amp;lt;math&amp;gt;v = r&amp;lt;/math&amp;gt;. Изначально &amp;lt;math&amp;gt;|subtree(v)| = n&amp;lt;/math&amp;gt;. Среди всех детей &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; выберем вершину &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; с максимальным размером поддерева. Если &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид, то положим &amp;lt;math&amp;gt;v = u&amp;lt;/math&amp;gt; и продолжим выбор нового u, иначе {{---}} остановимся. Докажем, что мы в какой-то момент остановимся. Пусть в призвольный момент времени &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид и размер её наддерева меньше &amp;lt;math&amp;gt;\dfrac{n}{2}&amp;lt;/math&amp;gt;, значит максимальное поддерево имеет размер больше чем &amp;lt;math&amp;gt;\dfrac{n}{2}&amp;lt;/math&amp;gt;, т.е. &amp;lt;math&amp;gt;|subtree(u)| &amp;gt; \dfrac{n}{2}&amp;lt;/math&amp;gt;, а значит размер &amp;quot;наддерева&amp;quot; вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; равен &amp;lt;tex&amp;gt;n - |subtree(u)| &amp;lt; \dfrac{n}{2}&amp;lt;/tex&amp;gt;. При этом теперь размер любого поддерева, на которое распадется дерево t при удалении вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; не превосходит &amp;lt;math&amp;gt;|subtree(u)| - 1&amp;lt;/math&amp;gt;, т.к. наддерево имеет размер меньше, чем поддерево &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, а любое поддерево вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; имеет хотя бы на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; вершину меньше (сама вершина &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;). По индукции получаем, что в любой момент времени размер наддерева вершины v меньше &amp;lt;math&amp;gt;\dfrac{n}{2}&amp;lt;/math&amp;gt;, значит мы будем спускаться только вниз по дереву &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, и при переходе к вершине &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} сыну &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; размер максимального поддерева уменьшится как минимум на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Значит не более чем за &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; шагов наши действия прекратятся и мы окажемся в центроиде дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
Итак, мы конструктивно доказали существование центроида и привели линейный относительно размера дерева алгоритм его нахождения. &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Реализация ===&lt;br /&gt;
&lt;br /&gt;
Поиск центроида в дереве:&lt;br /&gt;
&lt;br /&gt;
  '''int''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int''' sz[n])&lt;br /&gt;
     max_subtree = -1&lt;br /&gt;
     '''for''' u : children(v)&lt;br /&gt;
          '''if''' sz[u] &amp;gt; sz[v] / 2 and sz[u] &amp;gt; sz[max_subtree] &amp;lt;font color=green&amp;gt;// считаем sz[-1] = 0 &amp;lt;/font &amp;gt;&lt;br /&gt;
               max_subtree = u&lt;br /&gt;
     '''if''' max_subtree == -1&lt;br /&gt;
          return v&lt;br /&gt;
     '''else''' &lt;br /&gt;
          '''return''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz)&lt;br /&gt;
&lt;br /&gt;
Шаблон решения произвольной задачи на статическую центроидную декомпозицию:&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int''' sz[n])&lt;br /&gt;
     c = &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz) &amp;lt;font color=green&amp;gt;// находим c {{---}} центроид поддерева вершины v &amp;lt;/font &amp;gt;&lt;br /&gt;
     ch = children[c]&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{deleteEdges}&amp;lt;/tex&amp;gt;(c, children) &amp;lt;font color=green&amp;gt;// удаляем все ребра между вершиной с и детьми, чтобы мы не смогли из детей попасть в с. &lt;br /&gt;
         Это полезно делать, если решение подзадачи для поддерева предполагает проход dfs &amp;lt;/font &amp;gt;&lt;br /&gt;
     '''for''' c2 : ch[c]&lt;br /&gt;
          &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;(children, c2, sz)&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{mergeSolution}&amp;lt;/tex&amp;gt;(children, c2) &amp;lt;font color=green&amp;gt;// решаем для текущего поддерева &amp;lt;/font &amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Динамическая центроидная декомпозиция (дерево центроидной декомпозиции) ==&lt;br /&gt;
Теперь вернемся ко второй задаче введения. Для ее решения мы пользовались динамичекой версией «разделяй и властвуй» {{---}} деревом отрезков. В предыдущем пункте мы определили статический аналог «разделяй и властвуй» для дерева. Теперь обобщим этот метод для динамических задач.&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Деревом центроидов (или центроидной декомпозицией)''' дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; называют дерево &amp;lt;math&amp;gt;T(t)&amp;lt;/math&amp;gt;, построенное на вершинах дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; следующим образом:&lt;br /&gt;
* Пусть вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} центроид дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Объявим его корнем нового дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пусть при удалении вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; из дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; оно распалось на поддеревья &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, тогда детьми вершины c объявим &amp;lt;tex&amp;gt;T(t_1), T(t_t),\dots, T(t_k)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
=== Свойства центроидной декомпозиции ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
'''Свойства центроидной декомпозиции''':&lt;br /&gt;
# Глубина дерева центроидов не превосходит &amp;lt;tex&amp;gt;log(n)&amp;lt;/tex&amp;gt;. &lt;br /&gt;
# Каждая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; является центроидом одного из поддеревьев дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждая вершина дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; поддеревьям дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (еще говорят, что вершина принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; центроидам дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, или что эти центроиды содержат вершину)&lt;br /&gt;
# Для любых вершин &amp;lt;tex&amp;gt;u, v \in T (u \neq v)&amp;lt;/tex&amp;gt; верно ровно одно из следующих трех утверждений: &lt;br /&gt;
#*&amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;&lt;br /&gt;
#* &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;&lt;br /&gt;
#* &amp;lt;tex&amp;gt;T(u) \cap T(v) = \emptyset &amp;lt;/tex&amp;gt;&lt;br /&gt;
# Простой путь между любой парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; содержит центроид &amp;lt;tex&amp;gt;c \in T(t)&amp;lt;/tex&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
# Действительно, т.к. размер поддерева &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; не превосходит &amp;lt;tex&amp;gt;\dfrac{|subtree(c)|}{2}&amp;lt;/tex&amp;gt;, то при спуске в каждую следующую вершину на пути к любому листу в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; размер поддерева вершины, в которой мы сейчас находимся, уменьшается как минимум на &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;. Значит длина всего пути до листа не превосходит &amp;lt;math&amp;gt;log(n)&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
# Второе свойство очевидно из построения дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, т.к. если вершина принадлежит дереву центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то она является центроидом, а из построения &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы знаем, что каждая вершина исходного дерева принадлежит и дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Третье свойство {{---}} прямое следствие первых двух, т.к. вершина принадлежит любому центроиду &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; т.и т.т., когда c {{---}} отец вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов. Т.к. вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; точно принадлежит дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (свойство 2), то она лежит на каком-то пути в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, причем все ее родители (центроиды) ее содержат. А по свойству 1 длина любого вертикального (и даже простого) пути есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
# Четвертое свойство очевидно из того, что &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; {{---}} дерево. Т.к. &amp;lt;math&amp;gt;T(u)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;T(v)&amp;lt;/math&amp;gt; {{---}} поддеревья различных вершин дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то либо они не пересекаются, либо &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;, либо &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Для доказательства последнего свойства выберем в качестве вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;lca(u, v)&amp;lt;/math&amp;gt; в дереве центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Покажем, что так выбранная вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удовлетворяет заявленным свойствам. То, что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt; {{---}} очевидно по определению &amp;lt;math&amp;gt;lca&amp;lt;/math&amp;gt;, т.к. каждый предок любой вершины в дереве центроидов содержит эту вершину. Теперь докажем, что &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежит на пути между парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt;. Т.к. &amp;lt;math&amp;gt;c = lca(u, v)&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то из &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; нет ребра в такого сына, который содержит одновременно &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в своем поддереве (в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;), значит после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; разделится на несколько поддеревьев, таких что вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; окажутся в разных компонентах связности. А значит найдется такое ребро &amp;lt;math&amp;gt;(c, x)&amp;lt;/math&amp;gt;, которое принадлежало пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, но после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удалилось. Это доказывает то, что вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежала на пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
С помощью описанных свойств дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы можем решить задачу 2 для дерева:&lt;br /&gt;
&lt;br /&gt;
=== Пример решения задачи с помощью центроидной декомпозиции === &lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин. В каждый момент времени любая вершина дерева может быть либо помечена, либо нет. Изначально помечена только вершина номер &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Дан список из &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; запросов: &lt;br /&gt;
* Вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; пометили.&lt;br /&gt;
* С вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; сняли пометку.&lt;br /&gt;
* Найти номер ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины.&lt;br /&gt;
}}&lt;br /&gt;
(Задача C со сборов СПБ к РОИ-2016&amp;lt;ref&amp;gt;[http://neerc.ifmo.ru/school/camp-2016/problems/20160324a1.pdf  Сайт с задачами Санкт-Петербургских сборов к РОИ 2016]&amp;lt;/ref&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
'''Решение:'''&lt;br /&gt;
&lt;br /&gt;
Построим центроидную декомпозицию &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Изначально посчитаем для каждого центроида, содержащего вершину &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt; посчитаем расстояние до вершины &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Для этого воспользуемся [[Метод_двоичного_подъема|методом двоичных подъемов для поиска lca пары вершин в дереве]], а также тем фактом, что если глубина &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве определена как расстояние от корня до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то длина пути между парой вершин &amp;lt;math&amp;gt;(u, v)&amp;lt;/math&amp;gt; есть &amp;lt;tex&amp;gt;len(u, v) = h(u) + h(v) - 2 \cdot h(lca(u, v))&amp;lt;/tex&amp;gt;. Если изначально предпосчитать проходом [[Обход_в_глубину,_цвета_вершин| dfs]] величины &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, то ответ на запрос &amp;lt;math&amp;gt;len(u, v)&amp;lt;/math&amp;gt; можно делать за время &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с &amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt;доп. памятью. Также можно воспользоваться техникой [[Сведение_задачи_LCA_к_задаче_RMQ|сведения задачи LCA к RMQ]] и решить с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; времени на запрос. Теперь научимся отвечать на запросы. Из последнего свойства центроидной декомпозиции видно, что если &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} искомая ближайшая помеченная вершина к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то путь между ними содержит центроид &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;, причем &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок одновременно вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве&amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Поэтому заведем [[Красно-черное_дерево|двоичное дерево поиска]] для каждого центроида &amp;lt;math&amp;gt;c \in T&amp;lt;/math&amp;gt;. В этой структуре для каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; будем хранить пары &amp;lt;math&amp;gt;(len(c, u), u)&amp;lt;/math&amp;gt; для всех помеченных вершин &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в поддереве центроидов &amp;lt;math&amp;gt;T(c)&amp;lt;/math&amp;gt;. Когда приходит запрос пометить вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} добавим в структуру данных для всех предков &amp;lt;math&amp;gt;p_c&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; пары &amp;lt;math&amp;gt;(len(p_c, v), v)&amp;lt;/math&amp;gt;. Мы совершим &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; добавлений, затратив &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; действий на каждое. Запрос снятия пометки с вершины обрабатывается аналогичными удалениями. Запрос поиска ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины {{---}} это запрос поиска вершины u, такой что  величина &amp;lt;math&amp;gt;len(c, u) + len(c, v)&amp;lt;/math&amp;gt; минимальна, где &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов (по пятому свойству, нас интересуют именно такие &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;). Этот запрос занимает так же &amp;lt;tex&amp;gt;O(log^2(n))&amp;lt;/tex&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
Итак, мы научились решать задачу с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и временной сложностью &amp;lt;math&amp;gt;O(log^2(n))&amp;lt;/math&amp;gt; на запрос любого типа с предварительным предпосчетом за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Варианты хранения центроидной декомпозиции ==&lt;br /&gt;
Для хранения дерева центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; есть &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; подхода, имеющих свои преимущества и недостатки:&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева запомним величину &amp;lt;math&amp;gt;p_v&amp;lt;/math&amp;gt; {{---}} номер предка вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Этот подход наиболее экономный по памяти (&amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;), но уступает в скорости и функциональности.&lt;br /&gt;
&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева будем хранить весь массив предков &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt; в дереве центроидов.&lt;br /&gt;
&lt;br /&gt;
Этот подход уступает в количестве необходимой дополнительной памяти (&amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt; суммарно), но имеет ряд преимуществ:&lt;br /&gt;
&lt;br /&gt;
#При проходе по массиву предков фиксированной вершины будет выигрыш в скорости работы, т.к. весь массив будет лежать непрерывным блоком данных и следовательно будет закэширован&lt;br /&gt;
# На массиве предков можно строить различные структуры данных (такие как, например, дерево отрезков) для быстрого (в случае с деревом отрезков &amp;lt;math&amp;gt;O(log(log(n)))&amp;lt;/math&amp;gt; на запрос) поиска предка с необходимыми свойствами. Так, например, в описанной выше задаче про помеченные вершины наибольшего общего предка можно искать методом двоичных подъемов за &amp;lt;math&amp;gt;O(log(log(n))&amp;lt;/math&amp;gt; на запрос, т.к. размер массива предков есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; (по свойству 1 центроидной декомпозиции). Используя эту оптимизацию можно получить время &amp;lt;math&amp;gt;O(log(n)) + O(log(log(n)) = O(log(n))&amp;lt;/math&amp;gt; на запрос нахождения ближайшей помеченной вершины. Чтобы добиться улучшенной асимптотики для запросов изменения можно хранить дерево отрезков на каждом из путей &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt;, в каждой вершине которого хранить двоичное дерево поиска и поддерживать отложенные операции. Тогда ответ на эти запросы будет занимать &amp;lt;math&amp;gt;O(log(n) \cdot log(log(n))&amp;lt;/math&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
&lt;br /&gt;
*[[:Дерево_отрезков._Построение|Дерево отрезков]]&lt;br /&gt;
*[[:Heavy-light_декомпозиция|Heavy-light декомпозиция]]&lt;br /&gt;
*[[:Метод_двоичного_подъема| Метод двоичного подъема]]&lt;br /&gt;
&lt;br /&gt;
== Примечания == &lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы и структуры данных]]&lt;/div&gt;</summary>
		<author><name>92.255.113.52</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61642</id>
		<title>Centroid decomposition</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61642"/>
				<updated>2017-06-14T22:27:15Z</updated>
		
		<summary type="html">&lt;p&gt;92.255.113.52: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Centroid decomposition (рус. центроидная декомпозиция) {{---}} это структура данных, позволяющая отвечать на запросы на дереве. Чаще всего это запросы, связанные с нахождением функции на вершинах, связанных неравенством на расстояние между ними в дереве. Также иногда применяется для запросов на путях в дереве.&lt;br /&gt;
&lt;br /&gt;
== Введение ==&lt;br /&gt;
Рассмотрим &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; задачи на обычном массиве (в дальнейшем мы будем их обобщать на случай дерева):&lt;br /&gt;
&lt;br /&gt;
Задача &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть массив &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; положительных целых чисел из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов. Также дано число &amp;lt;tex&amp;gt;W \geqslant 0&amp;lt;/tex&amp;gt; и число &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; индексов массива, таких что &amp;lt;tex&amp;gt;|j - i| \leqslant l &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\sum_{k=i}^{j} a_k \leqslant W&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
Задача &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть прямая дорога, на которой расположены &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; городов. В некоторых городах есть госпитали, которые могут принимать больных. Поступают запросы вида:&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;, в котором находится больной и требуется найти такой город &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; может принимать больных и &amp;lt;tex&amp;gt;|u - v|&amp;lt;/tex&amp;gt; минимально возможное.&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что больше он не будет принимать больных&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что теперь он может принимать больных&lt;br /&gt;
}}&lt;br /&gt;
Для начала решим обе задачи. &lt;br /&gt;
Первая задача решается методом [[Сортировка_слиянием|«разделяй и властвуй»]] {{---}} давайте разделим массив &amp;lt;tex&amp;gt;a[0 \dots n-1]&amp;lt;/tex&amp;gt; на 2 массива &amp;lt;tex&amp;gt;a[0\dots\dfrac{n}{2} - 1]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;a[\dfrac{n}{2} \dots n-1]&amp;lt;/tex&amp;gt; и рекурсивно решим задачу для каждого из них. Осталось научиться находить количество искомых пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt;, таких что &amp;lt;tex&amp;gt;i &amp;lt; \dfrac{n}{2}, j \geqslant \dfrac{n}{2}&amp;lt;/tex&amp;gt;. Для этого воспользуемся другой известной техникой {{---}} методом двух указателей. Посчитаем массив префиксных сумм для правой половины &amp;lt;tex&amp;gt;pref[i] = \sum_{j=\dfrac{n}{2}}^{i} a_j&amp;lt;/tex&amp;gt; и суффиксных (&amp;lt;tex&amp;gt;suf[i] = \sum_{j=i}^{\dfrac{n}{2} + 1} a_j&amp;lt;/tex&amp;gt;) {{---}} для левой. Заведем два указателя (&amp;lt;tex&amp;gt;p_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;p_2&amp;lt;/tex&amp;gt;). Изначально установим &amp;lt;tex&amp;gt;p_1 = \dfrac{n}{2} - l + 1, p_2 = \dfrac{n}{2}&amp;lt;/tex&amp;gt;. Пока &amp;lt;tex&amp;gt;p_2 - 1&amp;gt; \dfrac{n}{2}&amp;lt;/tex&amp;gt; и&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;pref[p_2] + suf[p_1] &amp;gt; W &amp;lt;/tex&amp;gt; будем уменьшать &amp;lt;math&amp;gt;p_2&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Если после этого &amp;lt;math&amp;gt;pref[p_2] + suf[p_1] \leqslant W&amp;lt;/math&amp;gt;, то к ответу прибавим &amp;lt;math&amp;gt;(p_2 - \dfrac{n}{2} + 1) * (\dfrac{n}{2} - p_1)&amp;lt;/math&amp;gt;, посго, увеличим &amp;lt;math&amp;gt;p_1&amp;lt;/math&amp;gt; на &amp;lt;math/math&amp;gt;. Так будем делать, пока &amp;lt;math&amp;gt;p_1 &amp;lt; \dfrac{n}{2}&amp;lt;/math&amp;gt;. В конце сложим текущий ответ и ответы для половин массива {{---}} получим ответ на задачу. Асимптотика такого алгоритма: &amp;lt;tex&amp;gt;T(n) = 2 * T(n / 2) + O(n) = O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вторая задача имеет запросы на изменение и поэтому надо применить динамическую версию «разделяй и властвуй» {{---}} [[Дерево_отрезков._Построение|дерево отрезков]]. Построим дерево отрезков,&lt;br /&gt;
поддерживающее 2 вида запросов: присвоение в точке и минимум на отрезке. Изначально сделаем так, чтобы дереву отрезков соответствовал массив &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;b_i = 0&amp;lt;/tex&amp;gt;, если в i-м городе принимает госпиталь и &amp;lt;tex&amp;gt;b_i = 1&amp;lt;/tex&amp;gt; иначе. Когда в каком-то городе открывается/закрывается госпиталь {{---}} делаем запрос на изменение в дереве отрезков. Когда требуется узнать ближайщий госпиталь к &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-му городу, можно воспользоваться одной из следующих идей: &lt;br /&gt;
а) (&amp;lt;tex&amp;gt;O(n \cdot log^2(n)&amp;lt;/tex&amp;gt;) Бинарным поиском ищем ближайший слева и ближайший справа к i-му городу госпиталь (такой город &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, что &amp;lt;tex&amp;gt;\min\limits_{k=i..j}b_k= 1&amp;lt;/tex&amp;gt;). Для этого внутри бинарного поиска каждый раз делаем запрос на поиск минимума в дереве отрезков.  &lt;br /&gt;
б) (&amp;lt;tex&amp;gt;O(n \cdot log(n)&amp;lt;/tex&amp;gt;) Будем одним спуском/подъемом по дереву определять, куда нам нужно идти (в левое или правое поддерево), тем самым делая одновоременно и бинарный поиск, и спуск/подъем по дереву.&lt;br /&gt;
&lt;br /&gt;
== Статическая центроидная декомпозиция ==&lt;br /&gt;
Перейдем к обобщению поставленных задач на случай дерева. Начнем, как и полагается, с первой:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть взвешенное дерево &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин, в каждой вершине которого написаны положительные целые числа. Также по-прежнему даны числа &amp;lt;tex&amp;gt;W \geqslant 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; вершин дерева, таких что расстояние между ними не превосходит &amp;lt;math&amp;gt;l&amp;lt;/math&amp;gt; по числу ребер и не превосходит &amp;lt;math&amp;gt;W&amp;lt;/math&amp;gt; по сумме весов.&lt;br /&gt;
}}&lt;br /&gt;
(Задача D со сборов СПБ к РОИ-2016&amp;lt;ref&amp;gt;[http://neerc.ifmo.ru/school/camp-2016/problems/20160324a1.pdf  Сайт с задачами Санкт-Петербургских сборов к РОИ 2016]&amp;lt;/ref&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
Для решения новой задачи применим ту же идею, что и была до этого {{---}} разделяй и властвуй. Для этого нам потребуется следующий объект:&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|id=191213&lt;br /&gt;
|definition =&lt;br /&gt;
'''Центроидом дерева''' (англ. ''centroid'') называется такая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, после удаления которой дерево разбивается на несколько (&amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;) поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, таких что для каждого &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;: &amp;lt;tex&amp;gt;|t_i| \leqslant \dfrac{n}{2}&amp;lt;/tex&amp;gt;, т.е. размер каждого поддерева не превосходит половины размера исходного дерева.&lt;br /&gt;
}}&lt;br /&gt;
Итак, в случае дерева идея разделяй-и-властвуй из предыдущего пункта будет формулироваться так: найдем центроид. Предположим, что мы сумели найти центроид за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; {{---}} размер дерева. Тогда, как и в упрощенной версии задачи {{---}} рекурсивно найдем ответ для всех поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, после чего попытаемся найти недостающие пары вершин, находящиехя в разных поддеревьях и удовлетворяющих вопросу задачи. Для этого будем отвечать на следующие запросы: пусть мы сейчас считаем все пары, где первая из вершин находится в поддереве &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и мы в некоторой структуре данных &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; храним все вершины остальных деревьев (каждую вершину задаем парой &amp;lt;math&amp;gt;(depth(v), length(v))&amp;lt;/math&amp;gt; {{---}} глубина вершины и длина пути до нее из корня поддерева), расстояние до которых от корня их поддерева не превышает &amp;lt;math&amp;gt;min(l, n)&amp;lt;/math&amp;gt;. Тогда просто пройдемся по всем вершинам &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; поддерева &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и прибавим к ответу число вершин в структуре &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt;, таких, что &amp;lt;tex&amp;gt;depth(u) \leqslant l - depth(v)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;length(u) \leqslant l - length(v)&amp;lt;/tex&amp;gt;. Это двумерные запросы, на которые можно отвечать за &amp;lt;math&amp;gt;O(log^2(n)&amp;lt;/math&amp;gt; с помощью [[Многомерное_дерево_отрезков|2d-дерева отрезков]], либо за &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с помощью [[Перечисление_точек_в_произвольном_прямоугольнике_за_n_*_log_%5E(d_-_1)_n_(range_tree)|техники поиска точек в d-мерном пространстве]]. Также читателю предлагается придумать и более эффективные и простые способы решить эту подзадачу.&lt;br /&gt;
&lt;br /&gt;
Оценим итоговую асимптотику: &amp;lt;tex&amp;gt;T(n) = k * T(n / k) + O(n \cdot log(n))&amp;lt;/tex&amp;gt;. Решая это рекурентное соотношение, получим &amp;lt;math&amp;gt;O(n \cdot log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь докажем лемму о существовании центроида и опишем алгоритм его эффективного поиска.&lt;br /&gt;
&lt;br /&gt;
=== Лемма о существовании центроида и алгоритм его нахождения. ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
В любом дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; существует центроид.&lt;br /&gt;
|proof=&lt;br /&gt;
Рассмотрим корень дерева &amp;lt;math&amp;gt;(r)&amp;lt;/math&amp;gt;. Положим изначально &amp;lt;math&amp;gt;v = r&amp;lt;/math&amp;gt;. Изначально &amp;lt;math&amp;gt;|subtree(v)| = n&amp;lt;/math&amp;gt;. Среди всех детей &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; выберем вершину &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; с максимальным размером поддерева. Если &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид, то положим &amp;lt;math&amp;gt;v = u&amp;lt;/math&amp;gt; и продолжим выбор нового u, иначе {{---}} остановимся. Докажем, что мы в какой-то момент остановимся. Пусть в призвольный момент времени &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид и размер её наддерева меньше &amp;lt;math&amp;gt;\dfrac{n}{2}&amp;lt;/math&amp;gt;, значит максимальное поддерево имеет размер больше чем &amp;lt;math&amp;gt;\dfrac{n}{2}&amp;lt;/math&amp;gt;, т.е. &amp;lt;math&amp;gt;|subtree(u)| &amp;gt; \dfrac{n}{2}&amp;lt;/math&amp;gt;, а значит размер &amp;quot;наддерева&amp;quot; вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; равен &amp;lt;tex&amp;gt;n - |subtree(u)| &amp;lt; \dfrac{n}{2}&amp;lt;/tex&amp;gt;. При этом теперь размер любого поддерева, на которое распадется дерево t при удалении вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; не превосходит &amp;lt;math&amp;gt;|subtree(u)| - 1&amp;lt;/math&amp;gt;, т.к. наддерево имеет размер меньше, чем поддерево &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, а любое поддерево вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; имеет хотя бы на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; вершину меньше (сама вершина &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;). По индукции получаем, что в любой момент времени размер наддерева вершины v меньше &amp;lt;math&amp;gt;\dfrac{n}{2}&amp;lt;/math&amp;gt;, значит мы будем спускаться только вниз по дереву &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, и при переходе к вершине &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} сыну &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; размер максимального поддерева уменьшится как минимум на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Значит не более чем за &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; шагов наши действия прекратятся и мы окажемся в центроиде дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
Итак, мы конструктивно доказали существование центроида и привели линейный относительно размера дерева алгоритм его нахождения. &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Реализация ===&lt;br /&gt;
&lt;br /&gt;
Поиск центроида в дереве:&lt;br /&gt;
&lt;br /&gt;
  '''int''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int''' sz[n])&lt;br /&gt;
     max_subtree = -1&lt;br /&gt;
     '''for''' u : children(v)&lt;br /&gt;
          '''if''' sz[u] &amp;gt; sz[v] / 2 and sz[u] &amp;gt; sz[max_subtree] &amp;lt;font color=green&amp;gt;// считаем sz[-1] = 0 &amp;lt;/font &amp;gt;&lt;br /&gt;
               max_subtree = u&lt;br /&gt;
     '''if''' max_subtree == -1&lt;br /&gt;
          return v&lt;br /&gt;
     '''else''' &lt;br /&gt;
          '''return''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz)&lt;br /&gt;
&lt;br /&gt;
Шаблон решения произвольной задачи на статическую центроидную декомпозицию:&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int''' sz[n])&lt;br /&gt;
     c = &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz) &amp;lt;font color=green&amp;gt;// находим c {{---}} центроид поддерева вершины v &amp;lt;/font &amp;gt;&lt;br /&gt;
     ch = children[c]&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{deleteEdges}&amp;lt;/tex&amp;gt;(c, children) &amp;lt;font color=green&amp;gt;// удаляем все ребра между вершиной с и детьми, чтобы мы не смогли из детей попасть в с. &lt;br /&gt;
         Это полезно делать, если решение подзадачи для поддерева предполагает проход dfs &amp;lt;/font &amp;gt;&lt;br /&gt;
     '''for''' c2 : ch[c]&lt;br /&gt;
          &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;(children, c2, sz)&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{mergeSolution}&amp;lt;/tex&amp;gt;(children, c2) &amp;lt;font color=green&amp;gt;// решаем для текущего поддерева &amp;lt;/font &amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Динамическая центроидная декомпозиция (дерево центроидной декомпозиции) ==&lt;br /&gt;
Теперь вернемся ко второй задаче введения. Для ее решения мы пользовались динамичекой версией «разделяй и властвуй» {{---}} деревом отрезков. В предыдущем пункте мы определили статический аналог «разделяй и властвуй» для дерева. Теперь обобщим этот метод для динамических задач.&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Деревом центроидов (или центроидной декомпозицией)''' дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; называют дерево &amp;lt;math&amp;gt;T(t)&amp;lt;/math&amp;gt;, построенное на вершинах дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; следующим образом:&lt;br /&gt;
* Пусть вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} центроид дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Объявим его корнем нового дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пусть при удалении вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; из дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; оно распалось на поддеревья &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, тогда детьми вершины c объявим &amp;lt;tex&amp;gt;T(t_1), T(t_t),\dots, T(t_k)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
=== Свойства центроидной декомпозиции ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
'''Свойства центроидной декомпозиции''':&lt;br /&gt;
# Глубина дерева центроидов не превосходит &amp;lt;tex&amp;gt;log(n)&amp;lt;/tex&amp;gt;. &lt;br /&gt;
# Каждая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; является центроидом одного из поддеревьев дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждая вершина дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; поддеревьям дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (еще говорят, что вершина принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; центроидам дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, или что эти центроиды содержат вершину)&lt;br /&gt;
# Для любых вершин &amp;lt;tex&amp;gt;u, v \in T (u \neq v)&amp;lt;/tex&amp;gt; верно ровно одно из следующих трех утверждений: &lt;br /&gt;
#*&amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;&lt;br /&gt;
#* &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;&lt;br /&gt;
#* &amp;lt;tex&amp;gt;T(u) \cap T(v) = \emptyset &amp;lt;/tex&amp;gt;&lt;br /&gt;
# Простой путь между любой парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; содержит центроид &amp;lt;tex&amp;gt;c \in T(t)&amp;lt;/tex&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
# Действительно, т.к. размер поддерева &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; не превосходит &amp;lt;tex&amp;gt;\dfrac{|subtree(c)|}{2}&amp;lt;/tex&amp;gt;, то при спуске в каждую следующую вершину на пути к любому листу в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; размер поддерева вершины, в которой мы сейчас находимся, уменьшается как минимум на &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;. Значит длина всего пути до листа не превосходит &amp;lt;math&amp;gt;log(n)&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
# Второе свойство очевидно из построения дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, т.к. если вершина принадлежит дереву центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то она является центроидом, а из построения &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы знаем, что каждая вершина исходного дерева принадлежит и дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Третье свойство {{---}} прямое следствие первых двух, т.к. вершина принадлежит любому центроиду &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; т.и т.т., когда c {{---}} отец вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов. Т.к. вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; точно принадлежит дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (свойство 2), то она лежит на каком-то пути в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, причем все ее родители (центроиды) ее содержат. А по свойству 1 длина любого вертикального (и даже простого) пути есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
# Четвертое свойство очевидно из того, что &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; {{---}} дерево. Т.к. &amp;lt;math&amp;gt;T(u)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;T(v)&amp;lt;/math&amp;gt; {{---}} поддеревья различных вершин дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то либо они не пересекаются, либо &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;, либо &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Для доказательства последнего свойства выберем в качестве вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;lca(u, v)&amp;lt;/math&amp;gt; в дереве центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Покажем, что так выбранная вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удовлетворяет заявленным свойствам. То, что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt; {{---}} очевидно по определению &amp;lt;math&amp;gt;lca&amp;lt;/math&amp;gt;, т.к. каждый предок любой вершины в дереве центроидов содержит эту вершину. Теперь докажем, что &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежит на пути между парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt;. Т.к. &amp;lt;math&amp;gt;c = lca(u, v)&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то из &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; нет ребра в такого сына, который содержит одновременно &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в своем поддереве (в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;), значит после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; разделится на несколько поддеревьев, таких что вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; окажутся в разных компонентах связности. А значит найдется такое ребро &amp;lt;math&amp;gt;(c, x)&amp;lt;/math&amp;gt;, которое принадлежало пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, но после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удалилось. Это доказывает то, что вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежала на пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
С помощью описанных свойств дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы можем решить задачу 2 для дерева:&lt;br /&gt;
&lt;br /&gt;
=== Пример решения задачи с помощью центроидной декомпозиции === &lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин. В каждый момент времени любая вершина дерева может быть либо помечена, либо нет. Изначально помечена только вершина номер &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Дан список из &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; запросов: &lt;br /&gt;
* Вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; пометили.&lt;br /&gt;
* С вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; сняли пометку.&lt;br /&gt;
* Найти номер ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины.&lt;br /&gt;
}}&lt;br /&gt;
(Задача C со сборов СПБ к РОИ-2016&amp;lt;ref&amp;gt;[http://neerc.ifmo.ru/school/camp-2016/problems/20160324a1.pdf  Сайт с задачами Санкт-Петербургских сборов к РОИ 2016]&amp;lt;/ref&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
'''Решение:'''&lt;br /&gt;
&lt;br /&gt;
Построим центроидную декомпозицию &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Изначально посчитаем для каждого центроида, содержащего вершину &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt; посчитаем расстояние до вершины &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Для этого воспользуемся [[Метод_двоичного_подъема|методом двоичных подъемов для поиска lca пары вершин в дереве]], а также тем фактом, что если глубина &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве определена как расстояние от корня до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то длина пути между парой вершин &amp;lt;math&amp;gt;(u, v)&amp;lt;/math&amp;gt; есть &amp;lt;tex&amp;gt;len(u, v) = h(u) + h(v) - 2 \cdot h(lca(u, v))&amp;lt;/tex&amp;gt;. Если изначально предпосчитать проходом [[Обход_в_глубину,_цвета_вершин| dfs]] величины &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, то ответ на запрос &amp;lt;math&amp;gt;len(u, v)&amp;lt;/math&amp;gt; можно делать за время &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с &amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt;доп. памятью. Также можно воспользоваться техникой [[Сведение_задачи_LCA_к_задаче_RMQ|сведения задачи LCA к RMQ]] и решить с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; времени на запрос. Теперь научимся отвечать на запросы. Из последнего свойства центроидной декомпозиции видно, что если &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} искомая ближайшая помеченная вершина к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то путь между ними содержит центроид &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;, причем &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок одновременно вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве&amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Поэтому заведем [[Красно-черное_дерево|двоичное дерево поиска]] для каждого центроида &amp;lt;math&amp;gt;c \in T&amp;lt;/math&amp;gt;. В этой структуре для каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; будем хранить пары &amp;lt;math&amp;gt;(len(c, u), u)&amp;lt;/math&amp;gt; для всех помеченных вершин &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в поддереве центроидов &amp;lt;math&amp;gt;T(c)&amp;lt;/math&amp;gt;. Когда приходит запрос пометить вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} добавим в структуру данных для всех предков &amp;lt;math&amp;gt;p_c&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; пары &amp;lt;math&amp;gt;(len(p_c, v), v)&amp;lt;/math&amp;gt;. Мы совершим &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; добавлений, затратив &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; действий на каждое. Запрос снятия пометки с вершины обрабатывается аналогичными удалениями. Запрос поиска ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины {{---}} это запрос поиска вершины u, такой что  величина &amp;lt;math&amp;gt;len(c, u) + len(c, v)&amp;lt;/math&amp;gt; минимальна, где &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов (по пятому свойству, нас интересуют именно такие &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;). Этот запрос занимает так же &amp;lt;tex&amp;gt;O(log^2(n))&amp;lt;/tex&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
Итак, мы научились решать задачу с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и временной сложностью &amp;lt;math&amp;gt;O(log^2(n))&amp;lt;/math&amp;gt; на запрос любого типа с предварительным предпосчетом за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Варианты хранения центроидной декомпозиции ==&lt;br /&gt;
Для хранения дерева центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; есть &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; подхода, имеющих свои преимущества и недостатки:&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева запомним величину &amp;lt;math&amp;gt;p_v&amp;lt;/math&amp;gt; {{---}} номер предка вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Этот подход наиболее экономный по памяти (&amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;), но уступает в скорости и функциональности.&lt;br /&gt;
&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева будем хранить весь массив предков &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt; в дереве центроидов.&lt;br /&gt;
&lt;br /&gt;
Этот подход уступает в количестве необходимой дополнительной памяти (&amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt; суммарно), но имеет ряд преимуществ:&lt;br /&gt;
&lt;br /&gt;
#При проходе по массиву предков фиксированной вершины будет выигрыш в скорости работы, т.к. весь массив будет лежать непрерывным блоком данных и следовательно будет закэширован&lt;br /&gt;
# На массиве предков можно строить различные структуры данных (такие как, например, дерево отрезков) для быстрого (в случае с деревом отрезков &amp;lt;math&amp;gt;O(log(log(n)))&amp;lt;/math&amp;gt; на запрос) поиска предка с необходимыми свойствами. Так, например, в описанной выше задаче про помеченные вершины наибольшего общего предка можно искать методом двоичных подъемов за &amp;lt;math&amp;gt;O(log(log(n))&amp;lt;/math&amp;gt; на запрос, т.к. размер массива предков есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; (по свойству 1 центроидной декомпозиции). Используя эту оптимизацию можно получить время &amp;lt;math&amp;gt;O(log(n)) + O(log(log(n)) = O(log(n))&amp;lt;/math&amp;gt; на запрос нахождения ближайшей помеченной вершины. Чтобы добиться улучшенной асимптотики для запросов изменения можно хранить дерево отрезков на каждом из путей &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt;, в каждой вершине которого хранить двоичное дерево поиска и поддерживать отложенные операции. Тогда ответ на эти запросы будет занимать &amp;lt;math&amp;gt;O(log(n) \cdot log(log(n))&amp;lt;/math&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
&lt;br /&gt;
*[[:Дерево_отрезков._Построение|Дерево отрезков]]&lt;br /&gt;
*[[:Heavy-light_декомпозиция|Heavy-light декомпозиция]]&lt;br /&gt;
*[[:Метод_двоичного_подъема| Метод двоичного подъема]]&lt;br /&gt;
&lt;br /&gt;
== Примечания == &lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы и структуры данных]]&lt;/div&gt;</summary>
		<author><name>92.255.113.52</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61641</id>
		<title>Centroid decomposition</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61641"/>
				<updated>2017-06-14T22:26:30Z</updated>
		
		<summary type="html">&lt;p&gt;92.255.113.52: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Centroid decomposition (рус. центроидная декомпозиция) {{---}} это структура данных, позволяющая отвечать на запросы на дереве. Чаще всего это запросы, связанные с нахождением функции на вершинах, связанных неравенством на расстояние между ними в дереве. Также иногда применяется для запросов на путях в дереве.&lt;br /&gt;
&lt;br /&gt;
== Введение ==&lt;br /&gt;
Рассмотрим &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; задачи на обычном массиве (в дальнейшем мы будем их обобщать на случай дерева):&lt;br /&gt;
&lt;br /&gt;
Задача &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть массив &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; положительных целых чисел из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов. Также дано число &amp;lt;tex&amp;gt;W \geqslant 0&amp;lt;/tex&amp;gt; и число &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; индексов массива, таких что &amp;lt;tex&amp;gt;|j - i| \leqslant l &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\sum_{k=i}^{j} a_k \leqslant W&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
Задача &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть прямая дорога, на которой расположены &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; городов. В некоторых городах есть госпитали, которые могут принимать больных. Поступают запросы вида:&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;, в котором находится больной и требуется найти такой город &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; может принимать больных и &amp;lt;tex&amp;gt;|u - v|&amp;lt;/tex&amp;gt; минимально возможное.&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что больше он не будет принимать больных&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что теперь он может принимать больных&lt;br /&gt;
}}&lt;br /&gt;
Для начала решим обе задачи. &lt;br /&gt;
Первая задача решается методом [[Сортировка_слиянием|«разделяй и властвуй»]] {{---}} давайте разделим массив &amp;lt;tex&amp;gt;a[0 \dots n-1]&amp;lt;/tex&amp;gt; на 2 массива &amp;lt;tex&amp;gt;a[0\dots\frac{n}{2} - 1]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;a[\frac{n}{2} \dots n-1]&amp;lt;/tex&amp;gt; и рекурсивно решим задачу для каждого из них. Осталось научиться находить количество искомых пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt;, таких что &amp;lt;tex&amp;gt;i &amp;lt; \frac{n}{2}, j \geqslant \frac{n}{2}&amp;lt;/tex&amp;gt;. Для этого воспользуемся другой известной техникой {{---}} методом двух указателей. Посчитаем массив префиксных сумм для правой половины &amp;lt;tex&amp;gt;pref[i] = \sum_{j=\frac{n}{2}}^{i} a_j&amp;lt;/tex&amp;gt; и суффиксных (&amp;lt;tex&amp;gt;suf[i] = \sum_{j=i}^{\frac{n}{2} + 1} a_j&amp;lt;/tex&amp;gt;) {{---}} для левой. Заведем два указателя (&amp;lt;tex&amp;gt;p_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;p_2&amp;lt;/tex&amp;gt;). Изначально установим &amp;lt;tex&amp;gt;p_1 = \frac{n}{2} - l + 1, p_2 = \frac{n}{2}&amp;lt;/tex&amp;gt;. Пока &amp;lt;tex&amp;gt;p_2 - 1&amp;gt; \frac{n}{2}&amp;lt;/tex&amp;gt; и&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;pref[p_2] + suf[p_1] &amp;gt; W &amp;lt;/tex&amp;gt; будем уменьшать &amp;lt;math&amp;gt;p_2&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Если после этого &amp;lt;math&amp;gt;pref[p_2] + suf[p_1] \leqslant W&amp;lt;/math&amp;gt;, то к ответу прибавим &amp;lt;math&amp;gt;(p_2 - \frac{n}{2} + 1) * (\frac{n}{2} - p_1)&amp;lt;/math&amp;gt;, посго, увеличим &amp;lt;math&amp;gt;p_1&amp;lt;/math&amp;gt; на &amp;lt;math/math&amp;gt;. Так будем делать, пока &amp;lt;math&amp;gt;p_1 &amp;lt; \frac{n}{2}&amp;lt;/math&amp;gt;. В конце сложим текущий ответ и ответы для половин массива {{---}} получим ответ на задачу. Асимптотика такого алгоритма: &amp;lt;tex&amp;gt;T(n) = 2 * T(n / 2) + O(n) = O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вторая задача имеет запросы на изменение и поэтому надо применить динамическую версию «разделяй и властвуй» {{---}} [[Дерево_отрезков._Построение|дерево отрезков]]. Построим дерево отрезков,&lt;br /&gt;
поддерживающее 2 вида запросов: присвоение в точке и минимум на отрезке. Изначально сделаем так, чтобы дереву отрезков соответствовал массив &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;b_i = 0&amp;lt;/tex&amp;gt;, если в i-м городе принимает госпиталь и &amp;lt;tex&amp;gt;b_i = 1&amp;lt;/tex&amp;gt; иначе. Когда в каком-то городе открывается/закрывается госпиталь {{---}} делаем запрос на изменение в дереве отрезков. Когда требуется узнать ближайщий госпиталь к &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-му городу, можно воспользоваться одной из следующих идей: &lt;br /&gt;
а) (&amp;lt;tex&amp;gt;O(n \cdot log^2(n)&amp;lt;/tex&amp;gt;) Бинарным поиском ищем ближайший слева и ближайший справа к i-му городу госпиталь (такой город &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, что &amp;lt;tex&amp;gt;\min\limits_{k=i..j}b_k= 1&amp;lt;/tex&amp;gt;). Для этого внутри бинарного поиска каждый раз делаем запрос на поиск минимума в дереве отрезков.  &lt;br /&gt;
б) (&amp;lt;tex&amp;gt;O(n \cdot log(n)&amp;lt;/tex&amp;gt;) Будем одним спуском/подъемом по дереву определять, куда нам нужно идти (в левое или правое поддерево), тем самым делая одновоременно и бинарный поиск, и спуск/подъем по дереву.&lt;br /&gt;
&lt;br /&gt;
== Статическая центроидная декомпозиция ==&lt;br /&gt;
Перейдем к обобщению поставленных задач на случай дерева. Начнем, как и полагается, с первой:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть взвешенное дерево &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин, в каждой вершине которого написаны положительные целые числа. Также по-прежнему даны числа &amp;lt;tex&amp;gt;W \geqslant 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; вершин дерева, таких что расстояние между ними не превосходит &amp;lt;math&amp;gt;l&amp;lt;/math&amp;gt; по числу ребер и не превосходит &amp;lt;math&amp;gt;W&amp;lt;/math&amp;gt; по сумме весов.&lt;br /&gt;
}}&lt;br /&gt;
(Задача D со сборов СПБ к РОИ-2016&amp;lt;ref&amp;gt;[http://neerc.ifmo.ru/school/camp-2016/problems/20160324a1.pdf  Сайт с задачами Санкт-Петербургских сборов к РОИ 2016]&amp;lt;/ref&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
Для решения новой задачи применим ту же идею, что и была до этого {{---}} разделяй и властвуй. Для этого нам потребуется следующий объект:&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|id=191213&lt;br /&gt;
|definition =&lt;br /&gt;
'''Центроидом дерева''' (англ. ''centroid'') называется такая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, после удаления которой дерево разбивается на несколько (&amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;) поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, таких что для каждого &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;: &amp;lt;tex&amp;gt;|t_i| \leqslant \frac{n}{2}&amp;lt;/tex&amp;gt;, т.е. размер каждого поддерева не превосходит половины размера исходного дерева.&lt;br /&gt;
}}&lt;br /&gt;
Итак, в случае дерева идея разделяй-и-властвуй из предыдущего пункта будет формулироваться так: найдем центроид. Предположим, что мы сумели найти центроид за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; {{---}} размер дерева. Тогда, как и в упрощенной версии задачи {{---}} рекурсивно найдем ответ для всех поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, после чего попытаемся найти недостающие пары вершин, находящиехя в разных поддеревьях и удовлетворяющих вопросу задачи. Для этого будем отвечать на следующие запросы: пусть мы сейчас считаем все пары, где первая из вершин находится в поддереве &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и мы в некоторой структуре данных &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; храним все вершины остальных деревьев (каждую вершину задаем парой &amp;lt;math&amp;gt;(depth(v), length(v))&amp;lt;/math&amp;gt; {{---}} глубина вершины и длина пути до нее из корня поддерева), расстояние до которых от корня их поддерева не превышает &amp;lt;math&amp;gt;min(l, n)&amp;lt;/math&amp;gt;. Тогда просто пройдемся по всем вершинам &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; поддерева &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и прибавим к ответу число вершин в структуре &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt;, таких, что &amp;lt;tex&amp;gt;depth(u) \leqslant l - depth(v)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;length(u) \leqslant l - length(v)&amp;lt;/tex&amp;gt;. Это двумерные запросы, на которые можно отвечать за &amp;lt;math&amp;gt;O(log^2(n)&amp;lt;/math&amp;gt; с помощью [[Многомерное_дерево_отрезков|2d-дерева отрезков]], либо за &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с помощью [[Перечисление_точек_в_произвольном_прямоугольнике_за_n_*_log_%5E(d_-_1)_n_(range_tree)|техники поиска точек в d-мерном пространстве]]. Также читателю предлагается придумать и более эффективные и простые способы решить эту подзадачу.&lt;br /&gt;
&lt;br /&gt;
Оценим итоговую асимптотику: &amp;lt;tex&amp;gt;T(n) = k * T(n / k) + O(n \cdot log(n))&amp;lt;/tex&amp;gt;. Решая это рекурентное соотношение, получим &amp;lt;math&amp;gt;O(n \cdot log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь докажем лемму о существовании центроида и опишем алгоритм его эффективного поиска.&lt;br /&gt;
&lt;br /&gt;
=== Лемма о существовании центроида и алгоритм его нахождения. ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
В любом дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; существует центроид.&lt;br /&gt;
|proof=&lt;br /&gt;
Рассмотрим корень дерева &amp;lt;math&amp;gt;(r)&amp;lt;/math&amp;gt;. Положим изначально &amp;lt;math&amp;gt;v = r&amp;lt;/math&amp;gt;. Изначально &amp;lt;math&amp;gt;|subtree(v)| = n&amp;lt;/math&amp;gt;. Среди всех детей &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; выберем вершину &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; с максимальным размером поддерева. Если &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид, то положим &amp;lt;math&amp;gt;v = u&amp;lt;/math&amp;gt; и продолжим выбор нового u, иначе {{---}} остановимся. Докажем, что мы в какой-то момент остановимся. Пусть в призвольный момент времени &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид и размер её наддерева меньше &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, значит максимальное поддерево имеет размер больше чем &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, т.е. &amp;lt;math&amp;gt;|subtree(u)| &amp;gt; \frac{n}{2}&amp;lt;/math&amp;gt;, а значит размер &amp;quot;наддерева&amp;quot; вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; равен &amp;lt;tex&amp;gt;n - |subtree(u)| &amp;lt; \frac{n}{2}&amp;lt;/tex&amp;gt;. При этом теперь размер любого поддерева, на которое распадется дерево t при удалении вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; не превосходит &amp;lt;math&amp;gt;|subtree(u)| - 1&amp;lt;/math&amp;gt;, т.к. наддерево имеет размер меньше, чем поддерево &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, а любое поддерево вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; имеет хотя бы на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; вершину меньше (сама вершина &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;). По индукции получаем, что в любой момент времени размер наддерева вершины v меньше &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, значит мы будем спускаться только вниз по дереву &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, и при переходе к вершине &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} сыну &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; размер максимального поддерева уменьшится как минимум на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Значит не более чем за &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; шагов наши действия прекратятся и мы окажемся в центроиде дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
Итак, мы конструктивно доказали существование центроида и привели линейный относительно размера дерева алгоритм его нахождения. &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Реализация ===&lt;br /&gt;
&lt;br /&gt;
Поиск центроида в дереве:&lt;br /&gt;
&lt;br /&gt;
  '''int''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int''' sz[n])&lt;br /&gt;
     max_subtree = -1&lt;br /&gt;
     '''for''' u : children(v)&lt;br /&gt;
          '''if''' sz[u] &amp;gt; sz[v] / 2 and sz[u] &amp;gt; sz[max_subtree] &amp;lt;font color=green&amp;gt;// считаем sz[-1] = 0 &amp;lt;/font &amp;gt;&lt;br /&gt;
               max_subtree = u&lt;br /&gt;
     '''if''' max_subtree == -1&lt;br /&gt;
          return v&lt;br /&gt;
     '''else''' &lt;br /&gt;
          '''return''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz)&lt;br /&gt;
&lt;br /&gt;
Шаблон решения произвольной задачи на статическую центроидную декомпозицию:&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int''' sz[n])&lt;br /&gt;
     c = &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz) &amp;lt;font color=green&amp;gt;// находим c {{---}} центроид поддерева вершины v &amp;lt;/font &amp;gt;&lt;br /&gt;
     ch = children[c]&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{deleteEdges}&amp;lt;/tex&amp;gt;(c, children) &amp;lt;font color=green&amp;gt;// удаляем все ребра между вершиной с и детьми, чтобы мы не смогли из детей попасть в с. &lt;br /&gt;
         Это полезно делать, если решение подзадачи для поддерева предполагает проход dfs &amp;lt;/font &amp;gt;&lt;br /&gt;
     '''for''' c2 : ch[c]&lt;br /&gt;
          &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;(children, c2, sz)&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{mergeSolution}&amp;lt;/tex&amp;gt;(children, c2) &amp;lt;font color=green&amp;gt;// решаем для текущего поддерева &amp;lt;/font &amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Динамическая центроидная декомпозиция (дерево центроидной декомпозиции) ==&lt;br /&gt;
Теперь вернемся ко второй задаче введения. Для ее решения мы пользовались динамичекой версией «разделяй и властвуй» {{---}} деревом отрезков. В предыдущем пункте мы определили статический аналог «разделяй и властвуй» для дерева. Теперь обобщим этот метод для динамических задач.&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Деревом центроидов (или центроидной декомпозицией)''' дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; называют дерево &amp;lt;math&amp;gt;T(t)&amp;lt;/math&amp;gt;, построенное на вершинах дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; следующим образом:&lt;br /&gt;
* Пусть вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} центроид дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Объявим его корнем нового дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пусть при удалении вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; из дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; оно распалось на поддеревья &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, тогда детьми вершины c объявим &amp;lt;tex&amp;gt;T(t_1), T(t_t),\dots, T(t_k)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
=== Свойства центроидной декомпозиции ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
'''Свойства центроидной декомпозиции''':&lt;br /&gt;
# Глубина дерева центроидов не превосходит &amp;lt;tex&amp;gt;log(n)&amp;lt;/tex&amp;gt;. &lt;br /&gt;
# Каждая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; является центроидом одного из поддеревьев дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждая вершина дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; поддеревьям дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (еще говорят, что вершина принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; центроидам дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, или что эти центроиды содержат вершину)&lt;br /&gt;
# Для любых вершин &amp;lt;tex&amp;gt;u, v \in T (u \neq v)&amp;lt;/tex&amp;gt; верно ровно одно из следующих трех утверждений: &lt;br /&gt;
#*&amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;&lt;br /&gt;
#* &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;&lt;br /&gt;
#* &amp;lt;tex&amp;gt;T(u) \cap T(v) = \emptyset &amp;lt;/tex&amp;gt;&lt;br /&gt;
# Простой путь между любой парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; содержит центроид &amp;lt;tex&amp;gt;c \in T(t)&amp;lt;/tex&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
# Действительно, т.к. размер поддерева &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; не превосходит &amp;lt;tex&amp;gt;\frac{|subtree(c)|}{2}&amp;lt;/tex&amp;gt;, то при спуске в каждую следующую вершину на пути к любому листу в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; размер поддерева вершины, в которой мы сейчас находимся, уменьшается как минимум на &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;. Значит длина всего пути до листа не превосходит &amp;lt;math&amp;gt;log(n)&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
# Второе свойство очевидно из построения дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, т.к. если вершина принадлежит дереву центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то она является центроидом, а из построения &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы знаем, что каждая вершина исходного дерева принадлежит и дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Третье свойство {{---}} прямое следствие первых двух, т.к. вершина принадлежит любому центроиду &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; т.и т.т., когда c {{---}} отец вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов. Т.к. вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; точно принадлежит дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (свойство 2), то она лежит на каком-то пути в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, причем все ее родители (центроиды) ее содержат. А по свойству 1 длина любого вертикального (и даже простого) пути есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
# Четвертое свойство очевидно из того, что &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; {{---}} дерево. Т.к. &amp;lt;math&amp;gt;T(u)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;T(v)&amp;lt;/math&amp;gt; {{---}} поддеревья различных вершин дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то либо они не пересекаются, либо &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;, либо &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Для доказательства последнего свойства выберем в качестве вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;lca(u, v)&amp;lt;/math&amp;gt; в дереве центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Покажем, что так выбранная вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удовлетворяет заявленным свойствам. То, что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt; {{---}} очевидно по определению &amp;lt;math&amp;gt;lca&amp;lt;/math&amp;gt;, т.к. каждый предок любой вершины в дереве центроидов содержит эту вершину. Теперь докажем, что &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежит на пути между парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt;. Т.к. &amp;lt;math&amp;gt;c = lca(u, v)&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то из &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; нет ребра в такого сына, который содержит одновременно &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в своем поддереве (в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;), значит после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; разделится на несколько поддеревьев, таких что вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; окажутся в разных компонентах связности. А значит найдется такое ребро &amp;lt;math&amp;gt;(c, x)&amp;lt;/math&amp;gt;, которое принадлежало пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, но после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удалилось. Это доказывает то, что вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежала на пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
С помощью описанных свойств дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы можем решить задачу 2 для дерева:&lt;br /&gt;
&lt;br /&gt;
=== Пример решения задачи с помощью центроидной декомпозиции === &lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин. В каждый момент времени любая вершина дерева может быть либо помечена, либо нет. Изначально помечена только вершина номер &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Дан список из &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; запросов: &lt;br /&gt;
* Вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; пометили.&lt;br /&gt;
* С вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; сняли пометку.&lt;br /&gt;
* Найти номер ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины.&lt;br /&gt;
}}&lt;br /&gt;
(Задача C со сборов СПБ к РОИ-2016&amp;lt;ref&amp;gt;[http://neerc.ifmo.ru/school/camp-2016/problems/20160324a1.pdf  Сайт с задачами Санкт-Петербургских сборов к РОИ 2016]&amp;lt;/ref&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
'''Решение:'''&lt;br /&gt;
&lt;br /&gt;
Построим центроидную декомпозицию &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Изначально посчитаем для каждого центроида, содержащего вершину &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt; посчитаем расстояние до вершины &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Для этого воспользуемся [[Метод_двоичного_подъема|методом двоичных подъемов для поиска lca пары вершин в дереве]], а также тем фактом, что если глубина &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве определена как расстояние от корня до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то длина пути между парой вершин &amp;lt;math&amp;gt;(u, v)&amp;lt;/math&amp;gt; есть &amp;lt;tex&amp;gt;len(u, v) = h(u) + h(v) - 2 \cdot h(lca(u, v))&amp;lt;/tex&amp;gt;. Если изначально предпосчитать проходом [[Обход_в_глубину,_цвета_вершин| dfs]] величины &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, то ответ на запрос &amp;lt;math&amp;gt;len(u, v)&amp;lt;/math&amp;gt; можно делать за время &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с &amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt;доп. памятью. Также можно воспользоваться техникой [[Сведение_задачи_LCA_к_задаче_RMQ|сведения задачи LCA к RMQ]] и решить с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; времени на запрос. Теперь научимся отвечать на запросы. Из последнего свойства центроидной декомпозиции видно, что если &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} искомая ближайшая помеченная вершина к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то путь между ними содержит центроид &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;, причем &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок одновременно вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве&amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Поэтому заведем [[Красно-черное_дерево|двоичное дерево поиска]] для каждого центроида &amp;lt;math&amp;gt;c \in T&amp;lt;/math&amp;gt;. В этой структуре для каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; будем хранить пары &amp;lt;math&amp;gt;(len(c, u), u)&amp;lt;/math&amp;gt; для всех помеченных вершин &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в поддереве центроидов &amp;lt;math&amp;gt;T(c)&amp;lt;/math&amp;gt;. Когда приходит запрос пометить вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} добавим в структуру данных для всех предков &amp;lt;math&amp;gt;p_c&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; пары &amp;lt;math&amp;gt;(len(p_c, v), v)&amp;lt;/math&amp;gt;. Мы совершим &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; добавлений, затратив &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; действий на каждое. Запрос снятия пометки с вершины обрабатывается аналогичными удалениями. Запрос поиска ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины {{---}} это запрос поиска вершины u, такой что  величина &amp;lt;math&amp;gt;len(c, u) + len(c, v)&amp;lt;/math&amp;gt; минимальна, где &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов (по пятому свойству, нас интересуют именно такие &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;). Этот запрос занимает так же &amp;lt;tex&amp;gt;O(log^2(n))&amp;lt;/tex&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
Итак, мы научились решать задачу с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и временной сложностью &amp;lt;math&amp;gt;O(log^2(n))&amp;lt;/math&amp;gt; на запрос любого типа с предварительным предпосчетом за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Варианты хранения центроидной декомпозиции ==&lt;br /&gt;
Для хранения дерева центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; есть &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; подхода, имеющих свои преимущества и недостатки:&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева запомним величину &amp;lt;math&amp;gt;p_v&amp;lt;/math&amp;gt; {{---}} номер предка вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Этот подход наиболее экономный по памяти (&amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;), но уступает в скорости и функциональности.&lt;br /&gt;
&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева будем хранить весь массив предков &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt; в дереве центроидов.&lt;br /&gt;
&lt;br /&gt;
Этот подход уступает в количестве необходимой дополнительной памяти (&amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt; суммарно), но имеет ряд преимуществ:&lt;br /&gt;
&lt;br /&gt;
#При проходе по массиву предков фиксированной вершины будет выигрыш в скорости работы, т.к. весь массив будет лежать непрерывным блоком данных и следовательно будет закэширован&lt;br /&gt;
# На массиве предков можно строить различные структуры данных (такие как, например, дерево отрезков) для быстрого (в случае с деревом отрезков &amp;lt;math&amp;gt;O(log(log(n)))&amp;lt;/math&amp;gt; на запрос) поиска предка с необходимыми свойствами. Так, например, в описанной выше задаче про помеченные вершины наибольшего общего предка можно искать методом двоичных подъемов за &amp;lt;math&amp;gt;O(log(log(n))&amp;lt;/math&amp;gt; на запрос, т.к. размер массива предков есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; (по свойству 1 центроидной декомпозиции). Используя эту оптимизацию можно получить время &amp;lt;math&amp;gt;O(log(n)) + O(log(log(n)) = O(log(n))&amp;lt;/math&amp;gt; на запрос нахождения ближайшей помеченной вершины. Чтобы добиться улучшенной асимптотики для запросов изменения можно хранить дерево отрезков на каждом из путей &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt;, в каждой вершине которого хранить двоичное дерево поиска и поддерживать отложенные операции. Тогда ответ на эти запросы будет занимать &amp;lt;math&amp;gt;O(log(n) \cdot log(log(n))&amp;lt;/math&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
&lt;br /&gt;
*[[:Дерево_отрезков._Построение|Дерево отрезков]]&lt;br /&gt;
*[[:Heavy-light_декомпозиция|Heavy-light декомпозиция]]&lt;br /&gt;
*[[:Метод_двоичного_подъема| Метод двоичного подъема]]&lt;br /&gt;
&lt;br /&gt;
== Примечания == &lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы и структуры данных]]&lt;/div&gt;</summary>
		<author><name>92.255.113.52</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61640</id>
		<title>Centroid decomposition</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61640"/>
				<updated>2017-06-14T22:25:14Z</updated>
		
		<summary type="html">&lt;p&gt;92.255.113.52: /* Свойства центроидной декомпозиции */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Centroid decomposition (рус. центроидная декомпозиция) {{---}} это структура данных, позволяющая отвечать на запросы на дереве. Чаще всего это запросы, связанные с нахождением функции на вершинах, связанных неравенством на расстояние между ними в дереве. Также иногда применяется для запросов на путях в дереве.&lt;br /&gt;
&lt;br /&gt;
== Введение ==&lt;br /&gt;
Рассмотрим &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; задачи на обычном массиве (в дальнейшем мы будем их обобщать на случай дерева):&lt;br /&gt;
&lt;br /&gt;
Задача &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть массив &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; положительных целых чисел из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов. Также дано число &amp;lt;tex&amp;gt;W \geqslant 0&amp;lt;/tex&amp;gt; и число &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; индексов массива, таких что &amp;lt;tex&amp;gt;|j - i| \leqslant l &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\sum_{k=i}^{j} a_k \leqslant W&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
Задача &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть прямая дорога, на которой расположены &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; городов. В некоторых городах есть госпитали, которые могут принимать больных. Поступают запросы вида:&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;, в котором находится больной и требуется найти такой город &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; может принимать больных и &amp;lt;tex&amp;gt;|u - v|&amp;lt;/tex&amp;gt; минимально возможное.&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что больше он не будет принимать больных&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что теперь он может принимать больных&lt;br /&gt;
}}&lt;br /&gt;
Для начала решим обе задачи. &lt;br /&gt;
Первая задача решается методом [[Сортировка_слиянием|«разделяй и властвуй»]] {{---}} давайте разделим массив &amp;lt;tex&amp;gt;a[0 \dots n-1]&amp;lt;/tex&amp;gt; на 2 массива &amp;lt;tex&amp;gt;a[0\dots\frac{n}{2} - 1]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;a[\frac{n}{2} \dots n-1]&amp;lt;/tex&amp;gt; и рекурсивно решим задачу для каждого из них. Осталось научиться находить количество искомых пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt;, таких что &amp;lt;tex&amp;gt;i &amp;lt; \frac{n}{2}, j \geqslant \frac{n}{2}&amp;lt;/tex&amp;gt;. Для этого воспользуемся другой известной техникой {{---}} методом двух указателей. Посчитаем массив префиксных сумм для правой половины &amp;lt;tex&amp;gt;pref[i] = \sum_{j=\frac{n}{2}}^{i} a_j&amp;lt;/tex&amp;gt; и суффиксных (&amp;lt;tex&amp;gt;suf[i] = \sum_{j=i}^{\frac{n}{2} + 1} a_j&amp;lt;/tex&amp;gt;) {{---}} для левой. Заведем два указателя (&amp;lt;tex&amp;gt;p_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;p_2&amp;lt;/tex&amp;gt;). Изначально установим &amp;lt;tex&amp;gt;p_1 = \frac{n}{2} - l + 1, p_2 = \frac{n}{2}&amp;lt;/tex&amp;gt;. Пока &amp;lt;tex&amp;gt;p_2 - 1&amp;gt; \frac{n}{2}&amp;lt;/tex&amp;gt; и&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;pref[p_2] + suf[p_1] &amp;gt; W &amp;lt;/tex&amp;gt; будем уменьшать &amp;lt;math&amp;gt;p_2&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Если после этого &amp;lt;math&amp;gt;pref[p_2] + suf[p_1] \leqslant W&amp;lt;/math&amp;gt;, то к ответу прибавим &amp;lt;math&amp;gt;(p_2 - \frac{n}{2} + 1) * (\frac{n}{2} - p_1)&amp;lt;/math&amp;gt;, посго, увеличим &amp;lt;math&amp;gt;p_1&amp;lt;/math&amp;gt; на &amp;lt;math/math&amp;gt;. Так будем делать, пока &amp;lt;math&amp;gt;p_1 &amp;lt; \frac{n}{2}&amp;lt;/math&amp;gt;. В конце сложим текущий ответ и ответы для половин массива {{---}} получим ответ на задачу. Асимптотика такого алгоритма: &amp;lt;tex&amp;gt;T(n) = 2 * T(n / 2) + O(n) = O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вторая задача имеет запросы на изменение и поэтому надо применить динамическую версию «разделяй и властвуй» {{---}} [[Дерево_отрезков._Построение|дерево отрезков]]. Построим дерево отрезков,&lt;br /&gt;
поддерживающее 2 вида запросов: присвоение в точке и минимум на отрезке. Изначально сделаем так, чтобы дереву отрезков соответствовал массив &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;b_i = 0&amp;lt;/tex&amp;gt;, если в i-м городе принимает госпиталь и &amp;lt;tex&amp;gt;b_i = 1&amp;lt;/tex&amp;gt; иначе. Когда в каком-то городе открывается/закрывается госпиталь {{---}} делаем запрос на изменение в дереве отрезков. Когда требуется узнать ближайщий госпиталь к &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-му городу, можно воспользоваться одной из следующих идей: &lt;br /&gt;
а) (&amp;lt;tex&amp;gt;O(n \cdot log^2(n)&amp;lt;/tex&amp;gt;) Бинарным поиском ищем ближайший слева и ближайший справа к i-му городу госпиталь (такой город &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, что &amp;lt;tex&amp;gt;\min\limits_{k=i..j}b_k= 1&amp;lt;/tex&amp;gt;). Для этого внутри бинарного поиска каждый раз делаем запрос на поиск минимума в дереве отрезков.  &lt;br /&gt;
б) (&amp;lt;tex&amp;gt;O(n \cdot log(n)&amp;lt;/tex&amp;gt;) Будем одним спуском/подъемом по дереву определять, куда нам нужно идти (в левое или правое поддерево), тем самым делая одновоременно и бинарный поиск, и спуск/подъем по дереву.&lt;br /&gt;
&lt;br /&gt;
== Статическая центроидная декомпозиция ==&lt;br /&gt;
Перейдем к обобщению поставленных задач на случай дерева. Начнем, как и полагается, с первой:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть взвешенное дерево &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин, в каждой вершине которого написаны положительные целые числа. Также по-прежнему даны числа &amp;lt;tex&amp;gt;W \geqslant 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; вершин дерева, таких что расстояние между ними не превосходит &amp;lt;math&amp;gt;l&amp;lt;/math&amp;gt; по числу ребер и не превосходит &amp;lt;math&amp;gt;W&amp;lt;/math&amp;gt; по сумме весов.&lt;br /&gt;
}}&lt;br /&gt;
(Задача D со сборов СПБ к РОИ-2016&amp;lt;ref&amp;gt;[http://neerc.ifmo.ru/school/camp-2016/problems/20160324a1.pdf  Сайт с задачами Санкт-Петербургских сборов к РОИ 2016]&amp;lt;/ref&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
Для решения новой задачи применим ту же идею, что и была до этого {{---}} разделяй и властвуй. Для этого нам потребуется следующий объект:&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|id=191213&lt;br /&gt;
|definition =&lt;br /&gt;
'''Центроидом дерева''' (англ. ''centroid'') называется такая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, после удаления которой дерево разбивается на несколько (&amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;) поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, таких что для каждого &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;: &amp;lt;tex&amp;gt;|t_i| \leqslant \frac{n}{2}&amp;lt;/tex&amp;gt;, т.е. размер каждого поддерева не превосходит половины размера исходного дерева.&lt;br /&gt;
}}&lt;br /&gt;
Итак, в случае дерева идея разделяй-и-властвуй из предыдущего пункта будет формулироваться так: найдем центроид. Предположим, что мы сумели найти центроид за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; {{---}} размер дерева. Тогда, как и в упрощенной версии задачи {{---}} рекурсивно найдем ответ для всех поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, после чего попытаемся найти недостающие пары вершин, находящиехя в разных поддеревьях и удовлетворяющих вопросу задачи. Для этого будем отвечать на следующие запросы: пусть мы сейчас считаем все пары, где первая из вершин находится в поддереве &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и мы в некоторой структуре данных &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; храним все вершины остальных деревьев (каждую вершину задаем парой &amp;lt;math&amp;gt;(depth(v), length(v))&amp;lt;/math&amp;gt; {{---}} глубина вершины и длина пути до нее из корня поддерева), расстояние до которых от корня их поддерева не превышает &amp;lt;math&amp;gt;min(l, n)&amp;lt;/math&amp;gt;. Тогда просто пройдемся по всем вершинам &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; поддерева &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и прибавим к ответу число вершин в структуре &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt;, таких, что &amp;lt;tex&amp;gt;depth(u) \leqslant l - depth(v)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;length(u) \leqslant l - length(v)&amp;lt;/tex&amp;gt;. Это двумерные запросы, на которые можно отвечать за &amp;lt;math&amp;gt;O(log^2(n)&amp;lt;/math&amp;gt; с помощью [[Многомерное_дерево_отрезков|2d-дерева отрезков]], либо за &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с помощью [[Перечисление_точек_в_произвольном_прямоугольнике_за_n_*_log_%5E(d_-_1)_n_(range_tree)|техники поиска точек в d-мерном пространстве]]. Также читателю предлагается придумать и более эффективные и простые способы решить эту подзадачу.&lt;br /&gt;
&lt;br /&gt;
Оценим итоговую асимптотику: &amp;lt;tex&amp;gt;T(n) = k * T(n / k) + O(n \cdot log(n))&amp;lt;/tex&amp;gt;. Решая это рекурентное соотношение, получим &amp;lt;math&amp;gt;O(n \cdot log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь докажем лемму о существовании центроида и опишем алгоритм его эффективного поиска.&lt;br /&gt;
&lt;br /&gt;
=== Лемма о существовании центроида и алгоритм его нахождения. ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
В любом дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; существует центроид.&lt;br /&gt;
|proof=&lt;br /&gt;
Рассмотрим корень дерева &amp;lt;math&amp;gt;(r)&amp;lt;/math&amp;gt;. Положим изначально &amp;lt;math&amp;gt;v = r&amp;lt;/math&amp;gt;. Изначально &amp;lt;math&amp;gt;|subtree(v)| = n&amp;lt;/math&amp;gt;. Среди всех детей &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; выберем вершину &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; с максимальным размером поддерева. Если &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид, то положим &amp;lt;math&amp;gt;v = u&amp;lt;/math&amp;gt; и продолжим выбор нового u, иначе {{---}} остановимся. Докажем, что мы в какой-то момент остановимся. Пусть в призвольный момент времени &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид и размер её наддерева меньше &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, значит максимальное поддерево имеет размер больше чем &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, т.е. &amp;lt;math&amp;gt;|subtree(u)| &amp;gt; \frac{n}{2}&amp;lt;/math&amp;gt;, а значит размер &amp;quot;наддерева&amp;quot; вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; равен &amp;lt;tex&amp;gt;n - |subtree(u)| &amp;lt; \frac{n}{2}&amp;lt;/tex&amp;gt;. При этом теперь размер любого поддерева, на которое распадется дерево t при удалении вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; не превосходит &amp;lt;math&amp;gt;|subtree(u)| - 1&amp;lt;/math&amp;gt;, т.к. наддерево имеет размер меньше, чем поддерево &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, а любое поддерево вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; имеет хотя бы на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; вершину меньше (сама вершина &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;). По индукции получаем, что в любой момент времени размер наддерева вершины v меньше &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, значит мы будем спускаться только вниз по дереву &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, и при переходе к вершине &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} сыну &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; размер максимального поддерева уменьшится как минимум на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Значит не более чем за &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; шагов наши действия прекратятся и мы окажемся в центроиде дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
Итак, мы конструктивно доказали существование центроида и привели линейный относительно размера дерева алгоритм его нахождения. &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Реализация ===&lt;br /&gt;
&lt;br /&gt;
Поиск центроида в дереве:&lt;br /&gt;
&lt;br /&gt;
  '''int''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int''' sz[n])&lt;br /&gt;
     max_subtree = -1&lt;br /&gt;
     '''for''' u : children(v)&lt;br /&gt;
          '''if''' sz[u] &amp;gt; sz[v] / 2 and sz[u] &amp;gt; sz[max_subtree] &amp;lt;font color=green&amp;gt;// считаем sz[-1] = 0 &amp;lt;/font &amp;gt;&lt;br /&gt;
               max_subtree = u&lt;br /&gt;
     '''if''' max_subtree == -1&lt;br /&gt;
          return v&lt;br /&gt;
     '''else''' &lt;br /&gt;
          '''return''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz)&lt;br /&gt;
&lt;br /&gt;
Шаблон решения произвольной задачи на статическую центроидную декомпозицию:&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int''' sz[n])&lt;br /&gt;
     c = &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz) &amp;lt;font color=green&amp;gt;// находим c {{---}} центроид поддерева вершины v &amp;lt;/font &amp;gt;&lt;br /&gt;
     ch = children[c]&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{deleteEdges}&amp;lt;/tex&amp;gt;(c, children) &amp;lt;font color=green&amp;gt;// удаляем все ребра между вершиной с и детьми, чтобы мы не смогли из детей попасть в с. &lt;br /&gt;
         Это полезно делать, если решение подзадачи для поддерева предполагает проход dfs &amp;lt;/font &amp;gt;&lt;br /&gt;
     '''for''' c2 : ch[c]&lt;br /&gt;
          &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;(children, c2, sz)&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{mergeSolution}&amp;lt;/tex&amp;gt;(children, c2) &amp;lt;font color=green&amp;gt;// решаем для текущего поддерева &amp;lt;/font &amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Динамическая центроидная декомпозиция (дерево центроидной декомпозиции) ==&lt;br /&gt;
Теперь вернемся ко второй задаче введения. Для ее решения мы пользовались динамичекой версией «разделяй и властвуй» {{---}} деревом отрезков. В предыдущем пункте мы определили статический аналог «разделяй и властвуй» для дерева. Теперь обобщим этот метод для динамических задач.&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Деревом центроидов (или центроидной декомпозицией)''' дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; называют дерево &amp;lt;math&amp;gt;T(t)&amp;lt;/math&amp;gt;, построенное на вершинах дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; следующим образом:&lt;br /&gt;
* Пусть вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} центроид дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Объявим его корнем нового дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пусть при удалении вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; из дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; оно распалось на поддеревья &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, тогда детьми вершины c объявим &amp;lt;tex&amp;gt;T(t_1), T(t_t),\dots, T(t_k)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
=== Свойства центроидной декомпозиции ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
'''Свойства центроидной декомпозиции''':&lt;br /&gt;
# Глубина дерева центроидов не превосходит &amp;lt;tex&amp;gt;log(n)&amp;lt;/tex&amp;gt;. &lt;br /&gt;
# Каждая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; является центроидом одного из поддеревьев дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждая вершина дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; поддеревьям дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (еще говорят, что вершина принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; центроидам дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, или что эти центроиды содержат вершину)&lt;br /&gt;
# Для любых вершин &amp;lt;tex&amp;gt;u, v \in T (u \neq v)&amp;lt;/tex&amp;gt; верно ровно одно из следующих трех утверждений: &lt;br /&gt;
#*&amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;&lt;br /&gt;
#* &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;&lt;br /&gt;
#* &amp;lt;tex&amp;gt;T(u) \cap T(v) = \emptyset &amp;lt;/tex&amp;gt;&lt;br /&gt;
# Простой путь между любой парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; содержит центроид &amp;lt;tex&amp;gt;c \in T(t)&amp;lt;/tex&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
# Действительно, т.к. размер поддерева &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; не превосходит &amp;lt;tex&amp;gt;\frac{|subtree(c)|}{2}&amp;lt;/tex&amp;gt;, то при спуске в каждую следующую вершину на пути к любому листу в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; размер поддерева вершины, в которой мы сейчас находимся, уменьшается как минимум на &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;. Значит длина всего пути до листа не превосходит &amp;lt;math&amp;gt;log(n)&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
# Второе свойство очевидно из построения дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, т.к. если вершина принадлежит дереву центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то она является центроидом, а из построения &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы знаем, что каждая вершина исходного дерева принадлежит и дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Третье свойство {{---}} прямое следствие первых двух, т.к. вершина принадлежит любому центроиду &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; т.и т.т., когда c {{---}} отец вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов. Т.к. вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; точно принадлежит дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (свойство 2), то она лежит на каком-то пути в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, причем все ее родители (центроиды) ее содержат. А по свойству 1 длина любого вертикального (и даже простого) пути есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
# Четвертое свойство очевидно из того, что &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; {{---}} дерево. Т.к. &amp;lt;math&amp;gt;T(u)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;T(v)&amp;lt;/math&amp;gt; {{---}} поддеревья различных вершин дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то либо они не пересекаются, либо &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;, либо &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Для доказательства последнего свойства выберем в качестве вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;lca(u, v)&amp;lt;/math&amp;gt; в дереве центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Покажем, что так выбранная вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удовлетворяет заявленным свойствам. То, что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt; {{---}} очевидно по определению &amp;lt;math&amp;gt;lca&amp;lt;/math&amp;gt;, т.к. каждый предок любой вершины в дереве центроидов содержит эту вершину. Теперь докажем, что &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежит на пути между парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt;. Т.к. &amp;lt;math&amp;gt;c = lca(u, v)&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то из &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; нет ребра в такого сына, который содержит одновременно &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в своем поддереве (в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;), значит после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; разделится на несколько поддеревьев, таких что вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; окажутся в разных компонентах связности. А значит найдется такое ребро &amp;lt;math&amp;gt;(c, x)&amp;lt;/math&amp;gt;, которое принадлежало пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, но после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удалилось. Это доказывает то, что вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежала на пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
С помощью описанных свойств дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы можем решить задачу 2 для дерева:&lt;br /&gt;
&lt;br /&gt;
=== Пример решения задачи с помощью центроидной декомпозиции === &lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин. В каждый момент времени любая вершина дерева может быть либо помечена, либо нет. Изначально помечена только вершина номер &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Дан список из &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; запросов: &lt;br /&gt;
* Вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; пометили.&lt;br /&gt;
* С вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; сняли пометку.&lt;br /&gt;
* Найти номер ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины.&lt;br /&gt;
}}&lt;br /&gt;
(Задача C со сборов СПБ к РОИ-2016&amp;lt;ref&amp;gt;[http://neerc.ifmo.ru/school/camp-2016/problems/20160324a1.pdf  Сайт с задачами Санкт-Петербургских сборов к РОИ 2016]&amp;lt;/ref&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
'''Решение:'''&lt;br /&gt;
&lt;br /&gt;
Построим центроидную декомпозицию &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Изначально посчитаем для каждого центроида, содержащего вершину &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt; посчитаем расстояние до вершины &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Для этого воспользуемся [[Метод_двоичного_подъема|методом двоичных подъемов для поиска lca пары вершин в дереве]], а также тем фактом, что если глубина &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве определена как расстояние от корня до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то длина пути между парой вершин &amp;lt;math&amp;gt;(u, v)&amp;lt;/math&amp;gt; есть &amp;lt;tex&amp;gt;len(u, v) = h(u) + h(v) - 2 \cdot h(lca(u, v))&amp;lt;/tex&amp;gt;. Если изначально предпосчитать проходом [[Обход_в_глубину,_цвета_вершин| dfs]] величины &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, то ответ на запрос &amp;lt;math&amp;gt;len(u, v)&amp;lt;/math&amp;gt; можно делать за время &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с &amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt;доп. памятью. Также можно воспользоваться техникой [[Сведение_задачи_LCA_к_задаче_RMQ|сведения задачи LCA к RMQ]] и решить с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; времени на запрос. Теперь научимся отвечать на запросы. Из последнего свойства центроидной декомпозиции видно, что если &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} искомая ближайшая помеченная вершина к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то путь между ними содержит центроид &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;, причем &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок одновременно вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве&amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Поэтому заведем [[Красно-черное_дерево|двоичное дерево поиска]] для каждого центроида &amp;lt;math&amp;gt;c \in T&amp;lt;/math&amp;gt;. В этой структуре для каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; будем хранить пары &amp;lt;math&amp;gt;(len(c, u), u)&amp;lt;/math&amp;gt; для всех помеченных вершин &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в поддереве центроидов &amp;lt;math&amp;gt;T(c)&amp;lt;/math&amp;gt;. Когда приходит запрос пометить вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} добавим в структуру данных для всех предков &amp;lt;math&amp;gt;p_c&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; пары &amp;lt;math&amp;gt;(len(p_c, v), v)&amp;lt;/math&amp;gt;. Мы совершим &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; добавлений, затратив &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; действий на каждое. Запрос снятия пометки с вершины обрабатывается аналогичными удалениями. Запрос поиска ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины {{---}} это запрос поиска вершины u, такой что  величина &amp;lt;math&amp;gt;len(c, u) + len(c, v)&amp;lt;/math&amp;gt; минимальна, где &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов (по пятому свойству, нас интересуют именно такие &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;). Этот запрос занимает так же &amp;lt;tex&amp;gt;O(log^2(n))&amp;lt;/tex&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
Итак, мы научились решать задачу с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и временной сложностью &amp;lt;math&amp;gt;O(log^2(n))&amp;lt;/math&amp;gt; на запрос любого типа с предварительным предпосчетом за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Варианты хранения центроидной декомпозиции ==&lt;br /&gt;
Для хранения дерева центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; есть &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; подхода, имеющих свои преимущества и недостатки:&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева запомним величину &amp;lt;math&amp;gt;p_v&amp;lt;/math&amp;gt; {{---}} номер предка вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Этот подход наиболее экономный по памяти (&amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;), но уступает в скорости и функциональности.&lt;br /&gt;
&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева будем хранить весь массив предков &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt; в дереве центроидов.&lt;br /&gt;
&lt;br /&gt;
Этот подход уступает в количестве необходимой дополнительной памяти (&amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt; суммарно), но имеет ряд преимуществ:&lt;br /&gt;
&lt;br /&gt;
#При проходе по массиву предков фиксированной вершины будет выигрыш в скорости работы, т.к. весь массив будет лежать непрерывным блоком данных и следовательно будет закэширован&lt;br /&gt;
# На массиве предков можно строить различные структуры данных (такие как, например, дерево отрезков) для быстрого (в случае с деревом отрезков &amp;lt;math&amp;gt;O(log(log(n)))&amp;lt;/math&amp;gt; на запрос) поиска предка с необходимыми свойствами. Так, например, в описанной выше задаче про помеченные вершины наибольшего общего предка можно искать методом двоичных подъемов за &amp;lt;math&amp;gt;O(log(log(n))&amp;lt;/math&amp;gt; на запрос, т.к. размер массива предков есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; (по свойству 1 центроидной декомпозиции). Используя эту оптимизацию можно получить время &amp;lt;math&amp;gt;O(log(n)) + O(log(log(n)) = O(log(n))&amp;lt;/math&amp;gt; на запрос нахождения ближайшей помеченной вершины. Чтобы добиться улучшенной асимптотики для запросов изменения можно хранить дерево отрезков на каждом из путей &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt;, в каждой вершине которого хранить двоичное дерево поиска и поддерживать отложенные операции. Тогда ответ на эти запросы будет занимать &amp;lt;math&amp;gt;O(log(n) \cdot log(log(n))&amp;lt;/math&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
&lt;br /&gt;
*[[:Дерево_отрезков._Построение|Дерево отрезков]]&lt;br /&gt;
*[[:Heavy-light_декомпозиция|Heavy-light декомпозиция]]&lt;br /&gt;
*[[:Метод_двоичного_подъема| Метод двоичного подъема]]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы и структуры данных]]&lt;/div&gt;</summary>
		<author><name>92.255.113.52</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61639</id>
		<title>Centroid decomposition</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61639"/>
				<updated>2017-06-14T22:24:46Z</updated>
		
		<summary type="html">&lt;p&gt;92.255.113.52: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Centroid decomposition (рус. центроидная декомпозиция) {{---}} это структура данных, позволяющая отвечать на запросы на дереве. Чаще всего это запросы, связанные с нахождением функции на вершинах, связанных неравенством на расстояние между ними в дереве. Также иногда применяется для запросов на путях в дереве.&lt;br /&gt;
&lt;br /&gt;
== Введение ==&lt;br /&gt;
Рассмотрим &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; задачи на обычном массиве (в дальнейшем мы будем их обобщать на случай дерева):&lt;br /&gt;
&lt;br /&gt;
Задача &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть массив &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; положительных целых чисел из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов. Также дано число &amp;lt;tex&amp;gt;W \geqslant 0&amp;lt;/tex&amp;gt; и число &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; индексов массива, таких что &amp;lt;tex&amp;gt;|j - i| \leqslant l &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\sum_{k=i}^{j} a_k \leqslant W&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
Задача &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть прямая дорога, на которой расположены &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; городов. В некоторых городах есть госпитали, которые могут принимать больных. Поступают запросы вида:&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;, в котором находится больной и требуется найти такой город &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; может принимать больных и &amp;lt;tex&amp;gt;|u - v|&amp;lt;/tex&amp;gt; минимально возможное.&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что больше он не будет принимать больных&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что теперь он может принимать больных&lt;br /&gt;
}}&lt;br /&gt;
Для начала решим обе задачи. &lt;br /&gt;
Первая задача решается методом [[Сортировка_слиянием|«разделяй и властвуй»]] {{---}} давайте разделим массив &amp;lt;tex&amp;gt;a[0 \dots n-1]&amp;lt;/tex&amp;gt; на 2 массива &amp;lt;tex&amp;gt;a[0\dots\frac{n}{2} - 1]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;a[\frac{n}{2} \dots n-1]&amp;lt;/tex&amp;gt; и рекурсивно решим задачу для каждого из них. Осталось научиться находить количество искомых пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt;, таких что &amp;lt;tex&amp;gt;i &amp;lt; \frac{n}{2}, j \geqslant \frac{n}{2}&amp;lt;/tex&amp;gt;. Для этого воспользуемся другой известной техникой {{---}} методом двух указателей. Посчитаем массив префиксных сумм для правой половины &amp;lt;tex&amp;gt;pref[i] = \sum_{j=\frac{n}{2}}^{i} a_j&amp;lt;/tex&amp;gt; и суффиксных (&amp;lt;tex&amp;gt;suf[i] = \sum_{j=i}^{\frac{n}{2} + 1} a_j&amp;lt;/tex&amp;gt;) {{---}} для левой. Заведем два указателя (&amp;lt;tex&amp;gt;p_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;p_2&amp;lt;/tex&amp;gt;). Изначально установим &amp;lt;tex&amp;gt;p_1 = \frac{n}{2} - l + 1, p_2 = \frac{n}{2}&amp;lt;/tex&amp;gt;. Пока &amp;lt;tex&amp;gt;p_2 - 1&amp;gt; \frac{n}{2}&amp;lt;/tex&amp;gt; и&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;pref[p_2] + suf[p_1] &amp;gt; W &amp;lt;/tex&amp;gt; будем уменьшать &amp;lt;math&amp;gt;p_2&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Если после этого &amp;lt;math&amp;gt;pref[p_2] + suf[p_1] \leqslant W&amp;lt;/math&amp;gt;, то к ответу прибавим &amp;lt;math&amp;gt;(p_2 - \frac{n}{2} + 1) * (\frac{n}{2} - p_1)&amp;lt;/math&amp;gt;, посго, увеличим &amp;lt;math&amp;gt;p_1&amp;lt;/math&amp;gt; на &amp;lt;math/math&amp;gt;. Так будем делать, пока &amp;lt;math&amp;gt;p_1 &amp;lt; \frac{n}{2}&amp;lt;/math&amp;gt;. В конце сложим текущий ответ и ответы для половин массива {{---}} получим ответ на задачу. Асимптотика такого алгоритма: &amp;lt;tex&amp;gt;T(n) = 2 * T(n / 2) + O(n) = O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вторая задача имеет запросы на изменение и поэтому надо применить динамическую версию «разделяй и властвуй» {{---}} [[Дерево_отрезков._Построение|дерево отрезков]]. Построим дерево отрезков,&lt;br /&gt;
поддерживающее 2 вида запросов: присвоение в точке и минимум на отрезке. Изначально сделаем так, чтобы дереву отрезков соответствовал массив &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;b_i = 0&amp;lt;/tex&amp;gt;, если в i-м городе принимает госпиталь и &amp;lt;tex&amp;gt;b_i = 1&amp;lt;/tex&amp;gt; иначе. Когда в каком-то городе открывается/закрывается госпиталь {{---}} делаем запрос на изменение в дереве отрезков. Когда требуется узнать ближайщий госпиталь к &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-му городу, можно воспользоваться одной из следующих идей: &lt;br /&gt;
а) (&amp;lt;tex&amp;gt;O(n \cdot log^2(n)&amp;lt;/tex&amp;gt;) Бинарным поиском ищем ближайший слева и ближайший справа к i-му городу госпиталь (такой город &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, что &amp;lt;tex&amp;gt;\min\limits_{k=i..j}b_k= 1&amp;lt;/tex&amp;gt;). Для этого внутри бинарного поиска каждый раз делаем запрос на поиск минимума в дереве отрезков.  &lt;br /&gt;
б) (&amp;lt;tex&amp;gt;O(n \cdot log(n)&amp;lt;/tex&amp;gt;) Будем одним спуском/подъемом по дереву определять, куда нам нужно идти (в левое или правое поддерево), тем самым делая одновоременно и бинарный поиск, и спуск/подъем по дереву.&lt;br /&gt;
&lt;br /&gt;
== Статическая центроидная декомпозиция ==&lt;br /&gt;
Перейдем к обобщению поставленных задач на случай дерева. Начнем, как и полагается, с первой:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть взвешенное дерево &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин, в каждой вершине которого написаны положительные целые числа. Также по-прежнему даны числа &amp;lt;tex&amp;gt;W \geqslant 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; вершин дерева, таких что расстояние между ними не превосходит &amp;lt;math&amp;gt;l&amp;lt;/math&amp;gt; по числу ребер и не превосходит &amp;lt;math&amp;gt;W&amp;lt;/math&amp;gt; по сумме весов.&lt;br /&gt;
}}&lt;br /&gt;
(Задача D со сборов СПБ к РОИ-2016&amp;lt;ref&amp;gt;[http://neerc.ifmo.ru/school/camp-2016/problems/20160324a1.pdf  Сайт с задачами Санкт-Петербургских сборов к РОИ 2016]&amp;lt;/ref&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
Для решения новой задачи применим ту же идею, что и была до этого {{---}} разделяй и властвуй. Для этого нам потребуется следующий объект:&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|id=191213&lt;br /&gt;
|definition =&lt;br /&gt;
'''Центроидом дерева''' (англ. ''centroid'') называется такая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, после удаления которой дерево разбивается на несколько (&amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;) поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, таких что для каждого &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;: &amp;lt;tex&amp;gt;|t_i| \leqslant \frac{n}{2}&amp;lt;/tex&amp;gt;, т.е. размер каждого поддерева не превосходит половины размера исходного дерева.&lt;br /&gt;
}}&lt;br /&gt;
Итак, в случае дерева идея разделяй-и-властвуй из предыдущего пункта будет формулироваться так: найдем центроид. Предположим, что мы сумели найти центроид за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; {{---}} размер дерева. Тогда, как и в упрощенной версии задачи {{---}} рекурсивно найдем ответ для всех поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, после чего попытаемся найти недостающие пары вершин, находящиехя в разных поддеревьях и удовлетворяющих вопросу задачи. Для этого будем отвечать на следующие запросы: пусть мы сейчас считаем все пары, где первая из вершин находится в поддереве &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и мы в некоторой структуре данных &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; храним все вершины остальных деревьев (каждую вершину задаем парой &amp;lt;math&amp;gt;(depth(v), length(v))&amp;lt;/math&amp;gt; {{---}} глубина вершины и длина пути до нее из корня поддерева), расстояние до которых от корня их поддерева не превышает &amp;lt;math&amp;gt;min(l, n)&amp;lt;/math&amp;gt;. Тогда просто пройдемся по всем вершинам &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; поддерева &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и прибавим к ответу число вершин в структуре &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt;, таких, что &amp;lt;tex&amp;gt;depth(u) \leqslant l - depth(v)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;length(u) \leqslant l - length(v)&amp;lt;/tex&amp;gt;. Это двумерные запросы, на которые можно отвечать за &amp;lt;math&amp;gt;O(log^2(n)&amp;lt;/math&amp;gt; с помощью [[Многомерное_дерево_отрезков|2d-дерева отрезков]], либо за &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с помощью [[Перечисление_точек_в_произвольном_прямоугольнике_за_n_*_log_%5E(d_-_1)_n_(range_tree)|техники поиска точек в d-мерном пространстве]]. Также читателю предлагается придумать и более эффективные и простые способы решить эту подзадачу.&lt;br /&gt;
&lt;br /&gt;
Оценим итоговую асимптотику: &amp;lt;tex&amp;gt;T(n) = k * T(n / k) + O(n \cdot log(n))&amp;lt;/tex&amp;gt;. Решая это рекурентное соотношение, получим &amp;lt;math&amp;gt;O(n \cdot log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь докажем лемму о существовании центроида и опишем алгоритм его эффективного поиска.&lt;br /&gt;
&lt;br /&gt;
=== Лемма о существовании центроида и алгоритм его нахождения. ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
В любом дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; существует центроид.&lt;br /&gt;
|proof=&lt;br /&gt;
Рассмотрим корень дерева &amp;lt;math&amp;gt;(r)&amp;lt;/math&amp;gt;. Положим изначально &amp;lt;math&amp;gt;v = r&amp;lt;/math&amp;gt;. Изначально &amp;lt;math&amp;gt;|subtree(v)| = n&amp;lt;/math&amp;gt;. Среди всех детей &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; выберем вершину &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; с максимальным размером поддерева. Если &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид, то положим &amp;lt;math&amp;gt;v = u&amp;lt;/math&amp;gt; и продолжим выбор нового u, иначе {{---}} остановимся. Докажем, что мы в какой-то момент остановимся. Пусть в призвольный момент времени &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид и размер её наддерева меньше &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, значит максимальное поддерево имеет размер больше чем &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, т.е. &amp;lt;math&amp;gt;|subtree(u)| &amp;gt; \frac{n}{2}&amp;lt;/math&amp;gt;, а значит размер &amp;quot;наддерева&amp;quot; вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; равен &amp;lt;tex&amp;gt;n - |subtree(u)| &amp;lt; \frac{n}{2}&amp;lt;/tex&amp;gt;. При этом теперь размер любого поддерева, на которое распадется дерево t при удалении вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; не превосходит &amp;lt;math&amp;gt;|subtree(u)| - 1&amp;lt;/math&amp;gt;, т.к. наддерево имеет размер меньше, чем поддерево &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, а любое поддерево вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; имеет хотя бы на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; вершину меньше (сама вершина &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;). По индукции получаем, что в любой момент времени размер наддерева вершины v меньше &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, значит мы будем спускаться только вниз по дереву &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, и при переходе к вершине &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} сыну &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; размер максимального поддерева уменьшится как минимум на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Значит не более чем за &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; шагов наши действия прекратятся и мы окажемся в центроиде дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
Итак, мы конструктивно доказали существование центроида и привели линейный относительно размера дерева алгоритм его нахождения. &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Реализация ===&lt;br /&gt;
&lt;br /&gt;
Поиск центроида в дереве:&lt;br /&gt;
&lt;br /&gt;
  '''int''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int''' sz[n])&lt;br /&gt;
     max_subtree = -1&lt;br /&gt;
     '''for''' u : children(v)&lt;br /&gt;
          '''if''' sz[u] &amp;gt; sz[v] / 2 and sz[u] &amp;gt; sz[max_subtree] &amp;lt;font color=green&amp;gt;// считаем sz[-1] = 0 &amp;lt;/font &amp;gt;&lt;br /&gt;
               max_subtree = u&lt;br /&gt;
     '''if''' max_subtree == -1&lt;br /&gt;
          return v&lt;br /&gt;
     '''else''' &lt;br /&gt;
          '''return''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz)&lt;br /&gt;
&lt;br /&gt;
Шаблон решения произвольной задачи на статическую центроидную декомпозицию:&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int''' sz[n])&lt;br /&gt;
     c = &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz) &amp;lt;font color=green&amp;gt;// находим c {{---}} центроид поддерева вершины v &amp;lt;/font &amp;gt;&lt;br /&gt;
     ch = children[c]&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{deleteEdges}&amp;lt;/tex&amp;gt;(c, children) &amp;lt;font color=green&amp;gt;// удаляем все ребра между вершиной с и детьми, чтобы мы не смогли из детей попасть в с. &lt;br /&gt;
         Это полезно делать, если решение подзадачи для поддерева предполагает проход dfs &amp;lt;/font &amp;gt;&lt;br /&gt;
     '''for''' c2 : ch[c]&lt;br /&gt;
          &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;(children, c2, sz)&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{mergeSolution}&amp;lt;/tex&amp;gt;(children, c2) &amp;lt;font color=green&amp;gt;// решаем для текущего поддерева &amp;lt;/font &amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Динамическая центроидная декомпозиция (дерево центроидной декомпозиции) ==&lt;br /&gt;
Теперь вернемся ко второй задаче введения. Для ее решения мы пользовались динамичекой версией «разделяй и властвуй» {{---}} деревом отрезков. В предыдущем пункте мы определили статический аналог «разделяй и властвуй» для дерева. Теперь обобщим этот метод для динамических задач.&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Деревом центроидов (или центроидной декомпозицией)''' дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; называют дерево &amp;lt;math&amp;gt;T(t)&amp;lt;/math&amp;gt;, построенное на вершинах дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; следующим образом:&lt;br /&gt;
* Пусть вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} центроид дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Объявим его корнем нового дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пусть при удалении вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; из дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; оно распалось на поддеревья &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, тогда детьми вершины c объявим &amp;lt;tex&amp;gt;T(t_1), T(t_t),\dots, T(t_k)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
=== Свойства центроидной декомпозиции ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
'''Свойства центроидной декомпозиции''':&lt;br /&gt;
# Глубина дерева центроидов не превосходит &amp;lt;tex&amp;gt;log(n)&amp;lt;/tex&amp;gt;. &lt;br /&gt;
# Каждая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; является центроидом одного из поддеревьев дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждая вершина дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; поддеревьям дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (еще говорят, что вершина принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; центроидам дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, или что эти центроиды содержат вершину)&lt;br /&gt;
# Для любых вершин &amp;lt;tex&amp;gt;u, v \in T (u \neq v)&amp;lt;/tex&amp;gt; верно ровно одно из следующих трех утверждений: &lt;br /&gt;
##&amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;&lt;br /&gt;
## &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;&lt;br /&gt;
## &amp;lt;tex&amp;gt;T(u) \cap T(v) = \emptyset &amp;lt;/tex&amp;gt;&lt;br /&gt;
# Простой путь между любой парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; содержит центроид &amp;lt;tex&amp;gt;c \in T(t)&amp;lt;/tex&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
# Действительно, т.к. размер поддерева &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; не превосходит &amp;lt;tex&amp;gt;\frac{|subtree(c)|}{2}&amp;lt;/tex&amp;gt;, то при спуске в каждую следующую вершину на пути к любому листу в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; размер поддерева вершины, в которой мы сейчас находимся, уменьшается как минимум на &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;. Значит длина всего пути до листа не превосходит &amp;lt;math&amp;gt;log(n)&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
# Второе свойство очевидно из построения дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, т.к. если вершина принадлежит дереву центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то она является центроидом, а из построения &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы знаем, что каждая вершина исходного дерева принадлежит и дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Третье свойство {{---}} прямое следствие первых двух, т.к. вершина принадлежит любому центроиду &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; т.и т.т., когда c {{---}} отец вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов. Т.к. вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; точно принадлежит дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (свойство 2), то она лежит на каком-то пути в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, причем все ее родители (центроиды) ее содержат. А по свойству 1 длина любого вертикального (и даже простого) пути есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
# Четвертое свойство очевидно из того, что &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; {{---}} дерево. Т.к. &amp;lt;math&amp;gt;T(u)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;T(v)&amp;lt;/math&amp;gt; {{---}} поддеревья различных вершин дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то либо они не пересекаются, либо &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;, либо &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Для доказательства последнего свойства выберем в качестве вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;lca(u, v)&amp;lt;/math&amp;gt; в дереве центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Покажем, что так выбранная вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удовлетворяет заявленным свойствам. То, что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt; {{---}} очевидно по определению &amp;lt;math&amp;gt;lca&amp;lt;/math&amp;gt;, т.к. каждый предок любой вершины в дереве центроидов содержит эту вершину. Теперь докажем, что &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежит на пути между парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt;. Т.к. &amp;lt;math&amp;gt;c = lca(u, v)&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то из &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; нет ребра в такого сына, который содержит одновременно &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в своем поддереве (в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;), значит после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; разделится на несколько поддеревьев, таких что вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; окажутся в разных компонентах связности. А значит найдется такое ребро &amp;lt;math&amp;gt;(c, x)&amp;lt;/math&amp;gt;, которое принадлежало пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, но после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удалилось. Это доказывает то, что вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежала на пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
С помощью описанных свойств дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы можем решить задачу 2 для дерева:&lt;br /&gt;
&lt;br /&gt;
=== Пример решения задачи с помощью центроидной декомпозиции === &lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин. В каждый момент времени любая вершина дерева может быть либо помечена, либо нет. Изначально помечена только вершина номер &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Дан список из &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; запросов: &lt;br /&gt;
* Вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; пометили.&lt;br /&gt;
* С вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; сняли пометку.&lt;br /&gt;
* Найти номер ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины.&lt;br /&gt;
}}&lt;br /&gt;
(Задача C со сборов СПБ к РОИ-2016&amp;lt;ref&amp;gt;[http://neerc.ifmo.ru/school/camp-2016/problems/20160324a1.pdf  Сайт с задачами Санкт-Петербургских сборов к РОИ 2016]&amp;lt;/ref&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
'''Решение:'''&lt;br /&gt;
&lt;br /&gt;
Построим центроидную декомпозицию &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Изначально посчитаем для каждого центроида, содержащего вершину &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt; посчитаем расстояние до вершины &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Для этого воспользуемся [[Метод_двоичного_подъема|методом двоичных подъемов для поиска lca пары вершин в дереве]], а также тем фактом, что если глубина &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве определена как расстояние от корня до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то длина пути между парой вершин &amp;lt;math&amp;gt;(u, v)&amp;lt;/math&amp;gt; есть &amp;lt;tex&amp;gt;len(u, v) = h(u) + h(v) - 2 \cdot h(lca(u, v))&amp;lt;/tex&amp;gt;. Если изначально предпосчитать проходом [[Обход_в_глубину,_цвета_вершин| dfs]] величины &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, то ответ на запрос &amp;lt;math&amp;gt;len(u, v)&amp;lt;/math&amp;gt; можно делать за время &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с &amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt;доп. памятью. Также можно воспользоваться техникой [[Сведение_задачи_LCA_к_задаче_RMQ|сведения задачи LCA к RMQ]] и решить с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; времени на запрос. Теперь научимся отвечать на запросы. Из последнего свойства центроидной декомпозиции видно, что если &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} искомая ближайшая помеченная вершина к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то путь между ними содержит центроид &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;, причем &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок одновременно вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве&amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Поэтому заведем [[Красно-черное_дерево|двоичное дерево поиска]] для каждого центроида &amp;lt;math&amp;gt;c \in T&amp;lt;/math&amp;gt;. В этой структуре для каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; будем хранить пары &amp;lt;math&amp;gt;(len(c, u), u)&amp;lt;/math&amp;gt; для всех помеченных вершин &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в поддереве центроидов &amp;lt;math&amp;gt;T(c)&amp;lt;/math&amp;gt;. Когда приходит запрос пометить вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} добавим в структуру данных для всех предков &amp;lt;math&amp;gt;p_c&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; пары &amp;lt;math&amp;gt;(len(p_c, v), v)&amp;lt;/math&amp;gt;. Мы совершим &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; добавлений, затратив &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; действий на каждое. Запрос снятия пометки с вершины обрабатывается аналогичными удалениями. Запрос поиска ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины {{---}} это запрос поиска вершины u, такой что  величина &amp;lt;math&amp;gt;len(c, u) + len(c, v)&amp;lt;/math&amp;gt; минимальна, где &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов (по пятому свойству, нас интересуют именно такие &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;). Этот запрос занимает так же &amp;lt;tex&amp;gt;O(log^2(n))&amp;lt;/tex&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
Итак, мы научились решать задачу с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и временной сложностью &amp;lt;math&amp;gt;O(log^2(n))&amp;lt;/math&amp;gt; на запрос любого типа с предварительным предпосчетом за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Варианты хранения центроидной декомпозиции ==&lt;br /&gt;
Для хранения дерева центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; есть &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; подхода, имеющих свои преимущества и недостатки:&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева запомним величину &amp;lt;math&amp;gt;p_v&amp;lt;/math&amp;gt; {{---}} номер предка вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Этот подход наиболее экономный по памяти (&amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;), но уступает в скорости и функциональности.&lt;br /&gt;
&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева будем хранить весь массив предков &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt; в дереве центроидов.&lt;br /&gt;
&lt;br /&gt;
Этот подход уступает в количестве необходимой дополнительной памяти (&amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt; суммарно), но имеет ряд преимуществ:&lt;br /&gt;
&lt;br /&gt;
#При проходе по массиву предков фиксированной вершины будет выигрыш в скорости работы, т.к. весь массив будет лежать непрерывным блоком данных и следовательно будет закэширован&lt;br /&gt;
# На массиве предков можно строить различные структуры данных (такие как, например, дерево отрезков) для быстрого (в случае с деревом отрезков &amp;lt;math&amp;gt;O(log(log(n)))&amp;lt;/math&amp;gt; на запрос) поиска предка с необходимыми свойствами. Так, например, в описанной выше задаче про помеченные вершины наибольшего общего предка можно искать методом двоичных подъемов за &amp;lt;math&amp;gt;O(log(log(n))&amp;lt;/math&amp;gt; на запрос, т.к. размер массива предков есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; (по свойству 1 центроидной декомпозиции). Используя эту оптимизацию можно получить время &amp;lt;math&amp;gt;O(log(n)) + O(log(log(n)) = O(log(n))&amp;lt;/math&amp;gt; на запрос нахождения ближайшей помеченной вершины. Чтобы добиться улучшенной асимптотики для запросов изменения можно хранить дерево отрезков на каждом из путей &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt;, в каждой вершине которого хранить двоичное дерево поиска и поддерживать отложенные операции. Тогда ответ на эти запросы будет занимать &amp;lt;math&amp;gt;O(log(n) \cdot log(log(n))&amp;lt;/math&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
&lt;br /&gt;
*[[:Дерево_отрезков._Построение|Дерево отрезков]]&lt;br /&gt;
*[[:Heavy-light_декомпозиция|Heavy-light декомпозиция]]&lt;br /&gt;
*[[:Метод_двоичного_подъема| Метод двоичного подъема]]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы и структуры данных]]&lt;/div&gt;</summary>
		<author><name>92.255.113.52</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61638</id>
		<title>Centroid decomposition</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61638"/>
				<updated>2017-06-14T22:23:07Z</updated>
		
		<summary type="html">&lt;p&gt;92.255.113.52: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Centroid decomposition (рус. центроидная декомпозиция) {{---}} это структура данных, позволяющая отвечать на запросы на дереве. Чаще всего это запросы, связанные с нахождением функции на вершинах, связанных неравенством на расстояние между ними в дереве. Также иногда применяется для запросов на путях в дереве.&lt;br /&gt;
&lt;br /&gt;
== Введение ==&lt;br /&gt;
Рассмотрим &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; задачи на обычном массиве (в дальнейшем мы будем их обобщать на случай дерева):&lt;br /&gt;
&lt;br /&gt;
Задача &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть массив &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; положительных целых чисел из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов. Также дано число &amp;lt;tex&amp;gt;W \geqslant 0&amp;lt;/tex&amp;gt; и число &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; индексов массива, таких что &amp;lt;tex&amp;gt;|j - i| \leqslant l &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\sum_{k=i}^{j} a_k \leqslant W&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
Задача &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть прямая дорога, на которой расположены &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; городов. В некоторых городах есть госпитали, которые могут принимать больных. Поступают запросы вида:&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;, в котором находится больной и требуется найти такой город &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; может принимать больных и &amp;lt;tex&amp;gt;|u - v|&amp;lt;/tex&amp;gt; минимально возможное.&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что больше он не будет принимать больных&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что теперь он может принимать больных&lt;br /&gt;
}}&lt;br /&gt;
Для начала решим обе задачи. &lt;br /&gt;
Первая задача решается методом [[Сортировка_слиянием|«разделяй и властвуй»]] {{---}} давайте разделим массив &amp;lt;tex&amp;gt;a[0 \dots n-1]&amp;lt;/tex&amp;gt; на 2 массива &amp;lt;tex&amp;gt;a[0\dots\dfac{n}{2} - 1]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;a[\dfac{n}{2} \dots n-1]&amp;lt;/tex&amp;gt; и рекурсивно решим задачу для каждого из них. Осталось научиться находить количество искомых пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt;, таких что &amp;lt;tex&amp;gt;i &amp;lt; \dfac{n}{2}, j \geqslant \dfac{n}{2}&amp;lt;/tex&amp;gt;. Для этого воспользуемся другой известной техникой {{---}} методом двух указателей. Посчитаем массив префиксных сумм для правой половины &amp;lt;tex&amp;gt;pref[i] = \sum_{j=\dfac{n}{2}}^{i} a_j&amp;lt;/tex&amp;gt; и суффиксных (&amp;lt;tex&amp;gt;suf[i] = \sum_{j=i}^{\dfac{n}{2} + 1} a_j&amp;lt;/tex&amp;gt;) {{---}} для левой. Заведем два указателя (&amp;lt;tex&amp;gt;p_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;p_2&amp;lt;/tex&amp;gt;). Изначально установим &amp;lt;tex&amp;gt;p_1 = \dfac{n}{2} - l + 1, p_2 = \dfac{n}{2}&amp;lt;/tex&amp;gt;. Пока &amp;lt;tex&amp;gt;p_2 - 1&amp;gt; \dfac{n}{2}&amp;lt;/tex&amp;gt; и&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;pref[p_2] + suf[p_1] &amp;gt; W &amp;lt;/tex&amp;gt; будем уменьшать &amp;lt;math&amp;gt;p_2&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Если после этого &amp;lt;math&amp;gt;pref[p_2] + suf[p_1] \leqslant W&amp;lt;/math&amp;gt;, то к ответу прибавим &amp;lt;math&amp;gt;(p_2 - \dfac{n}{2} + 1) * (\dfac{n}{2} - p_1)&amp;lt;/math&amp;gt;, посго, увеличим &amp;lt;math&amp;gt;p_1&amp;lt;/math&amp;gt; на &amp;lt;math/math&amp;gt;. Так будем делать, пока &amp;lt;math&amp;gt;p_1 &amp;lt; \dfac{n}{2}&amp;lt;/math&amp;gt;. В конце сложим текущий ответ и ответы для половин массива {{---}} получим ответ на задачу. Асимптотика такого алгоритма: &amp;lt;tex&amp;gt;T(n) = 2 * T(n / 2) + O(n) = O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вторая задача имеет запросы на изменение и поэтому надо применить динамическую версию «разделяй и властвуй» {{---}} [[Дерево_отрезков._Построение|дерево отрезков]]. Построим дерево отрезков,&lt;br /&gt;
поддерживающее 2 вида запросов: присвоение в точке и минимум на отрезке. Изначально сделаем так, чтобы дереву отрезков соответствовал массив &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;b_i = 0&amp;lt;/tex&amp;gt;, если в i-м городе принимает госпиталь и &amp;lt;tex&amp;gt;b_i = 1&amp;lt;/tex&amp;gt; иначе. Когда в каком-то городе открывается/закрывается госпиталь {{---}} делаем запрос на изменение в дереве отрезков. Когда требуется узнать ближайщий госпиталь к &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-му городу, можно воспользоваться одной из следующих идей: &lt;br /&gt;
а) (&amp;lt;tex&amp;gt;O(n \cdot log^2(n)&amp;lt;/tex&amp;gt;) Бинарным поиском ищем ближайший слева и ближайший справа к i-му городу госпиталь (такой город &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, что &amp;lt;tex&amp;gt;\min\limits_{k=i..j}b_k= 1&amp;lt;/tex&amp;gt;). Для этого внутри бинарного поиска каждый раз делаем запрос на поиск минимума в дереве отрезков.  &lt;br /&gt;
б) (&amp;lt;tex&amp;gt;O(n \cdot log(n)&amp;lt;/tex&amp;gt;) Будем одним спуском/подъемом по дереву определять, куда нам нужно идти (в левое или правое поддерево), тем самым делая одновоременно и бинарный поиск, и спуск/подъем по дереву.&lt;br /&gt;
&lt;br /&gt;
== Статическая центроидная декомпозиция ==&lt;br /&gt;
Перейдем к обобщению поставленных задач на случай дерева. Начнем, как и полагается, с первой:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть взвешенное дерево &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин, в каждой вершине которого написаны положительные целые числа. Также по-прежнему даны числа &amp;lt;tex&amp;gt;W \geqslant 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; вершин дерева, таких что расстояние между ними не превосходит &amp;lt;math&amp;gt;l&amp;lt;/math&amp;gt; по числу ребер и не превосходит &amp;lt;math&amp;gt;W&amp;lt;/math&amp;gt; по сумме весов.&lt;br /&gt;
}}&lt;br /&gt;
(Задача D со сборов СПБ к РОИ-2016&amp;lt;ref&amp;gt;[http://neerc.ifmo.ru/school/camp-2016/problems/20160324a1.pdf  Сайт с задачами Санкт-Петербургских сборов к РОИ 2016]&amp;lt;/ref&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
Для решения новой задачи применим ту же идею, что и была до этого {{---}} разделяй и властвуй. Для этого нам потребуется следующий объект:&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|id=191213&lt;br /&gt;
|definition =&lt;br /&gt;
'''Центроидом дерева''' (англ. ''centroid'') называется такая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, после удаления которой дерево разбивается на несколько (&amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;) поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, таких что для каждого &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;: &amp;lt;tex&amp;gt;|t_i| \leqslant \dfac{n}{2}&amp;lt;/tex&amp;gt;, т.е. размер каждого поддерева не превосходит половины размера исходного дерева.&lt;br /&gt;
}}&lt;br /&gt;
Итак, в случае дерева идея разделяй-и-властвуй из предыдущего пункта будет формулироваться так: найдем центроид. Предположим, что мы сумели найти центроид за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; {{---}} размер дерева. Тогда, как и в упрощенной версии задачи {{---}} рекурсивно найдем ответ для всех поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, после чего попытаемся найти недостающие пары вершин, находящиехя в разных поддеревьях и удовлетворяющих вопросу задачи. Для этого будем отвечать на следующие запросы: пусть мы сейчас считаем все пары, где первая из вершин находится в поддереве &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и мы в некоторой структуре данных &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; храним все вершины остальных деревьев (каждую вершину задаем парой &amp;lt;math&amp;gt;(depth(v), length(v))&amp;lt;/math&amp;gt; {{---}} глубина вершины и длина пути до нее из корня поддерева), расстояние до которых от корня их поддерева не превышает &amp;lt;math&amp;gt;min(l, n)&amp;lt;/math&amp;gt;. Тогда просто пройдемся по всем вершинам &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; поддерева &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и прибавим к ответу число вершин в структуре &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt;, таких, что &amp;lt;tex&amp;gt;depth(u) \leqslant l - depth(v)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;length(u) \leqslant l - length(v)&amp;lt;/tex&amp;gt;. Это двумерные запросы, на которые можно отвечать за &amp;lt;math&amp;gt;O(log^2(n)&amp;lt;/math&amp;gt; с помощью [[Многомерное_дерево_отрезков|2d-дерева отрезков]], либо за &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с помощью [[Перечисление_точек_в_произвольном_прямоугольнике_за_n_*_log_%5E(d_-_1)_n_(range_tree)|техники поиска точек в d-мерном пространстве]]. Также читателю предлагается придумать и более эффективные и простые способы решить эту подзадачу.&lt;br /&gt;
&lt;br /&gt;
Оценим итоговую асимптотику: &amp;lt;tex&amp;gt;T(n) = k * T(n / k) + O(n \cdot log(n))&amp;lt;/tex&amp;gt;. Решая это рекурентное соотношение, получим &amp;lt;math&amp;gt;O(n \cdot log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь докажем лемму о существовании центроида и опишем алгоритм его эффективного поиска.&lt;br /&gt;
&lt;br /&gt;
=== Лемма о существовании центроида и алгоритм его нахождения. ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
В любом дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; существует центроид.&lt;br /&gt;
|proof=&lt;br /&gt;
Рассмотрим корень дерева &amp;lt;math&amp;gt;(r)&amp;lt;/math&amp;gt;. Положим изначально &amp;lt;math&amp;gt;v = r&amp;lt;/math&amp;gt;. Изначально &amp;lt;math&amp;gt;|subtree(v)| = n&amp;lt;/math&amp;gt;. Среди всех детей &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; выберем вершину &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; с максимальным размером поддерева. Если &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид, то положим &amp;lt;math&amp;gt;v = u&amp;lt;/math&amp;gt; и продолжим выбор нового u, иначе {{---}} остановимся. Докажем, что мы в какой-то момент остановимся. Пусть в призвольный момент времени &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид и размер её наддерева меньше &amp;lt;math&amp;gt;\dfac{n}{2}&amp;lt;/math&amp;gt;, значит максимальное поддерево имеет размер больше чем &amp;lt;math&amp;gt;\dfac{n}{2}&amp;lt;/math&amp;gt;, т.е. &amp;lt;math&amp;gt;|subtree(u)| &amp;gt; \dfac{n}{2}&amp;lt;/math&amp;gt;, а значит размер &amp;quot;наддерева&amp;quot; вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; равен &amp;lt;tex&amp;gt;n - |subtree(u)| &amp;lt; \dfac{n}{2}&amp;lt;/tex&amp;gt;. При этом теперь размер любого поддерева, на которое распадется дерево t при удалении вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; не превосходит &amp;lt;math&amp;gt;|subtree(u)| - 1&amp;lt;/math&amp;gt;, т.к. наддерево имеет размер меньше, чем поддерево &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, а любое поддерево вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; имеет хотя бы на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; вершину меньше (сама вершина &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;). По индукции получаем, что в любой момент времени размер наддерева вершины v меньше &amp;lt;math&amp;gt;\dfac{n}{2}&amp;lt;/math&amp;gt;, значит мы будем спускаться только вниз по дереву &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, и при переходе к вершине &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} сыну &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; размер максимального поддерева уменьшится как минимум на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Значит не более чем за &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; шагов наши действия прекратятся и мы окажемся в центроиде дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
Итак, мы конструктивно доказали существование центроида и привели линейный относительно размера дерева алгоритм его нахождения. &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Реализация ===&lt;br /&gt;
&lt;br /&gt;
Поиск центроида в дереве:&lt;br /&gt;
&lt;br /&gt;
  '''int''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int''' sz[n])&lt;br /&gt;
     max_subtree = -1&lt;br /&gt;
     '''for''' u : children(v)&lt;br /&gt;
          '''if''' sz[u] &amp;gt; sz[v] / 2 and sz[u] &amp;gt; sz[max_subtree] &amp;lt;font color=green&amp;gt;// считаем sz[-1] = 0 &amp;lt;/font &amp;gt;&lt;br /&gt;
               max_subtree = u&lt;br /&gt;
     '''if''' max_subtree == -1&lt;br /&gt;
          return v&lt;br /&gt;
     '''else''' &lt;br /&gt;
          '''return''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz)&lt;br /&gt;
&lt;br /&gt;
Шаблон решения произвольной задачи на статическую центроидную декомпозицию:&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int''' sz[n])&lt;br /&gt;
     c = &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz) &amp;lt;font color=green&amp;gt;// находим c {{---}} центроид поддерева вершины v &amp;lt;/font &amp;gt;&lt;br /&gt;
     ch = children[c]&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{deleteEdges}&amp;lt;/tex&amp;gt;(c, children) &amp;lt;font color=green&amp;gt;// удаляем все ребра между вершиной с и детьми, чтобы мы не смогли из детей попасть в с. &lt;br /&gt;
         Это полезно делать, если решение подзадачи для поддерева предполагает проход dfs &amp;lt;/font &amp;gt;&lt;br /&gt;
     '''for''' c2 : ch[c]&lt;br /&gt;
          &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;(children, c2, sz)&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{mergeSolution}&amp;lt;/tex&amp;gt;(children, c2) &amp;lt;font color=green&amp;gt;// решаем для текущего поддерева &amp;lt;/font &amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Динамическая центроидная декомпозиция (дерево центроидной декомпозиции) ==&lt;br /&gt;
Теперь вернемся ко второй задаче введения. Для ее решения мы пользовались динамичекой версией «разделяй и властвуй» {{---}} деревом отрезков. В предыдущем пункте мы определили статический аналог «разделяй и властвуй» для дерева. Теперь обобщим этот метод для динамических задач.&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Деревом центроидов (или центроидной декомпозицией)''' дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; называют дерево &amp;lt;math&amp;gt;T(t)&amp;lt;/math&amp;gt;, построенное на вершинах дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; следующим образом:&lt;br /&gt;
* Пусть вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} центроид дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Объявим его корнем нового дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пусть при удалении вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; из дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; оно распалось на поддеревья &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, тогда детьми вершины c объявим &amp;lt;tex&amp;gt;T(t_1), T(t_t),\dots, T(t_k)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
=== Свойства центроидной декомпозиции ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
'''Свойства центроидной декомпозиции''':&lt;br /&gt;
# Глубина дерева центроидов не превосходит &amp;lt;tex&amp;gt;log(n)&amp;lt;/tex&amp;gt;. &lt;br /&gt;
# Каждая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; является центроидом одного из поддеревьев дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждая вершина дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; поддеревьям дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (еще говорят, что вершина принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; центроидам дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, или что эти центроиды содержат вершину)&lt;br /&gt;
# Для любых вершин &amp;lt;tex&amp;gt;u, v \in T (u \neq v)&amp;lt;/tex&amp;gt; верно ровно одно из следующих трех утверждений: &lt;br /&gt;
##&amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;&lt;br /&gt;
## &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;&lt;br /&gt;
## &amp;lt;tex&amp;gt;T(u) \cap T(v) = \emptyset &amp;lt;/tex&amp;gt;&lt;br /&gt;
# Простой путь между любой парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; содержит центроид &amp;lt;tex&amp;gt;c \in T(t)&amp;lt;/tex&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
# Действительно, т.к. размер поддерева &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; не превосходит &amp;lt;tex&amp;gt;\dfac{|subtree(c)|}{2}&amp;lt;/tex&amp;gt;, то при спуске в каждую следующую вершину на пути к любому листу в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; размер поддерева вершины, в которой мы сейчас находимся, уменьшается как минимум на &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;. Значит длина всего пути до листа не превосходит &amp;lt;math&amp;gt;log(n)&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
# Второе свойство очевидно из построения дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, т.к. если вершина принадлежит дереву центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то она является центроидом, а из построения &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы знаем, что каждая вершина исходного дерева принадлежит и дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Третье свойство {{---}} прямое следствие первых двух, т.к. вершина принадлежит любому центроиду &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; т.и т.т., когда c {{---}} отец вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов. Т.к. вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; точно принадлежит дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (свойство 2), то она лежит на каком-то пути в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, причем все ее родители (центроиды) ее содержат. А по свойству 1 длина любого вертикального (и даже простого) пути есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
# Четвертое свойство очевидно из того, что &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; {{---}} дерево. Т.к. &amp;lt;math&amp;gt;T(u)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;T(v)&amp;lt;/math&amp;gt; {{---}} поддеревья различных вершин дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то либо они не пересекаются, либо &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;, либо &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Для доказательства последнего свойства выберем в качестве вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;lca(u, v)&amp;lt;/math&amp;gt; в дереве центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Покажем, что так выбранная вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удовлетворяет заявленным свойствам. То, что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt; {{---}} очевидно по определению &amp;lt;math&amp;gt;lca&amp;lt;/math&amp;gt;, т.к. каждый предок любой вершины в дереве центроидов содержит эту вершину. Теперь докажем, что &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежит на пути между парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt;. Т.к. &amp;lt;math&amp;gt;c = lca(u, v)&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то из &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; нет ребра в такого сына, который содержит одновременно &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в своем поддереве (в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;), значит после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; разделится на несколько поддеревьев, таких что вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; окажутся в разных компонентах связности. А значит найдется такое ребро &amp;lt;math&amp;gt;(c, x)&amp;lt;/math&amp;gt;, которое принадлежало пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, но после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удалилось. Это доказывает то, что вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежала на пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
С помощью описанных свойств дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы можем решить задачу 2 для дерева:&lt;br /&gt;
&lt;br /&gt;
=== Пример решения задачи с помощью центроидной декомпозиции === &lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин. В каждый момент времени любая вершина дерева может быть либо помечена, либо нет. Изначально помечена только вершина номер &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Дан список из &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; запросов: &lt;br /&gt;
* Вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; пометили.&lt;br /&gt;
* С вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; сняли пометку.&lt;br /&gt;
* Найти номер ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины.&lt;br /&gt;
}}&lt;br /&gt;
(Задача C со сборов СПБ к РОИ-2016&amp;lt;ref&amp;gt;[http://neerc.ifmo.ru/school/camp-2016/problems/20160324a1.pdf  Сайт с задачами Санкт-Петербургских сборов к РОИ 2016]&amp;lt;/ref&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
'''Решение:'''&lt;br /&gt;
&lt;br /&gt;
Построим центроидную декомпозицию &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Изначально посчитаем для каждого центроида, содержащего вершину &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt; посчитаем расстояние до вершины &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Для этого воспользуемся [[Метод_двоичного_подъема|методом двоичных подъемов для поиска lca пары вершин в дереве]], а также тем фактом, что если глубина &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве определена как расстояние от корня до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то длина пути между парой вершин &amp;lt;math&amp;gt;(u, v)&amp;lt;/math&amp;gt; есть &amp;lt;tex&amp;gt;len(u, v) = h(u) + h(v) - 2 \cdot h(lca(u, v))&amp;lt;/tex&amp;gt;. Если изначально предпосчитать проходом [[Обход_в_глубину,_цвета_вершин| dfs]] величины &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, то ответ на запрос &amp;lt;math&amp;gt;len(u, v)&amp;lt;/math&amp;gt; можно делать за время &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с &amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt;доп. памятью. Также можно воспользоваться техникой [[Сведение_задачи_LCA_к_задаче_RMQ|сведения задачи LCA к RMQ]] и решить с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; времени на запрос. Теперь научимся отвечать на запросы. Из последнего свойства центроидной декомпозиции видно, что если &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} искомая ближайшая помеченная вершина к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то путь между ними содержит центроид &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;, причем &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок одновременно вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве&amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Поэтому заведем [[Красно-черное_дерево|двоичное дерево поиска]] для каждого центроида &amp;lt;math&amp;gt;c \in T&amp;lt;/math&amp;gt;. В этой структуре для каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; будем хранить пары &amp;lt;math&amp;gt;(len(c, u), u)&amp;lt;/math&amp;gt; для всех помеченных вершин &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в поддереве центроидов &amp;lt;math&amp;gt;T(c)&amp;lt;/math&amp;gt;. Когда приходит запрос пометить вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} добавим в структуру данных для всех предков &amp;lt;math&amp;gt;p_c&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; пары &amp;lt;math&amp;gt;(len(p_c, v), v)&amp;lt;/math&amp;gt;. Мы совершим &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; добавлений, затратив &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; действий на каждое. Запрос снятия пометки с вершины обрабатывается аналогичными удалениями. Запрос поиска ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины {{---}} это запрос поиска вершины u, такой что  величина &amp;lt;math&amp;gt;len(c, u) + len(c, v)&amp;lt;/math&amp;gt; минимальна, где &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов (по пятому свойству, нас интересуют именно такие &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;). Этот запрос занимает так же &amp;lt;tex&amp;gt;O(log^2(n))&amp;lt;/tex&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
Итак, мы научились решать задачу с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и временной сложностью &amp;lt;math&amp;gt;O(log^2(n))&amp;lt;/math&amp;gt; на запрос любого типа с предварительным предпосчетом за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Варианты хранения центроидной декомпозиции ==&lt;br /&gt;
Для хранения дерева центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; есть &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; подхода, имеющих свои преимущества и недостатки:&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева запомним величину &amp;lt;math&amp;gt;p_v&amp;lt;/math&amp;gt; {{---}} номер предка вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Этот подход наиболее экономный по памяти (&amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;), но уступает в скорости и функциональности.&lt;br /&gt;
&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева будем хранить весь массив предков &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt; в дереве центроидов.&lt;br /&gt;
&lt;br /&gt;
Этот подход уступает в количестве необходимой дополнительной памяти (&amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt; суммарно), но имеет ряд преимуществ:&lt;br /&gt;
&lt;br /&gt;
#При проходе по массиву предков фиксированной вершины будет выигрыш в скорости работы, т.к. весь массив будет лежать непрерывным блоком данных и следовательно будет закэширован&lt;br /&gt;
# На массиве предков можно строить различные структуры данных (такие как, например, дерево отрезков) для быстрого (в случае с деревом отрезков &amp;lt;math&amp;gt;O(log(log(n)))&amp;lt;/math&amp;gt; на запрос) поиска предка с необходимыми свойствами. Так, например, в описанной выше задаче про помеченные вершины наибольшего общего предка можно искать методом двоичных подъемов за &amp;lt;math&amp;gt;O(log(log(n))&amp;lt;/math&amp;gt; на запрос, т.к. размер массива предков есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; (по свойству 1 центроидной декомпозиции). Используя эту оптимизацию можно получить время &amp;lt;math&amp;gt;O(log(n)) + O(log(log(n)) = O(log(n))&amp;lt;/math&amp;gt; на запрос нахождения ближайшей помеченной вершины. Чтобы добиться улучшенной асимптотики для запросов изменения можно хранить дерево отрезков на каждом из путей &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt;, в каждой вершине которого хранить двоичное дерево поиска и поддерживать отложенные операции. Тогда ответ на эти запросы будет занимать &amp;lt;math&amp;gt;O(log(n) \cdot log(log(n))&amp;lt;/math&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
&lt;br /&gt;
*[[:Дерево_отрезков._Построение|Дерево отрезков]]&lt;br /&gt;
*[[:Heavy-light_декомпозиция|Heavy-light декомпозиция]]&lt;br /&gt;
*[[:Метод_двоичного_подъема| Метод двоичного подъема]]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы и структуры данных]]&lt;/div&gt;</summary>
		<author><name>92.255.113.52</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61637</id>
		<title>Centroid decomposition</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61637"/>
				<updated>2017-06-14T21:57:22Z</updated>
		
		<summary type="html">&lt;p&gt;92.255.113.52: /* Статическая центроидная декомпозиция */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Centroid decomposition (рус. центроидная декомпозиция) {{---}} это структура данных, позволяющая отвечать на запросы на дереве. Чаще всего это запросы, связанные с нахождением функции на вершинах, связанных неравенством на расстояние между ними в дереве. Также иногда применяется для запросов на путях в дереве.&lt;br /&gt;
&lt;br /&gt;
== Введение ==&lt;br /&gt;
Рассмотрим &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; задачи на обычном массиве (в дальнейшем мы будем их обобщать на случай дерева):&lt;br /&gt;
&lt;br /&gt;
Задача &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть массив &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; положительных целых чисел из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов. Также дано число &amp;lt;tex&amp;gt;W \geqslant 0&amp;lt;/tex&amp;gt; и число &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; индексов массива, таких что &amp;lt;tex&amp;gt;|j - i| \leqslant l &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\sum_{k=i}^{j} a_k \leqslant W&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
Задача &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть прямая дорога, на которой расположены &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; городов. В некоторых городах есть госпитали, которые могут принимать больных. Поступают запросы вида :&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;, в котором находится больной и требуется найти такой город &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;|u - v|&amp;lt;/tex&amp;gt; минимально возможное.&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что больше он не будет принимать больных&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что теперь он может принимать больных&lt;br /&gt;
}}&lt;br /&gt;
Для начала решим обе задачи. &lt;br /&gt;
Первая задача решается методом [[Сортировка_слиянием|divide &amp;amp; conquer (рус. разделяй и властвуй)]] {{---}} давайте разделим массив &amp;lt;tex&amp;gt;a[0 \dots n-1]&amp;lt;/tex&amp;gt; на 2 массива &amp;lt;tex&amp;gt;a[0\dots\frac{n}{2} - 1]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;a[\frac{n}{2} \dots n-1]&amp;lt;/tex&amp;gt; и рекурсивно решим задачу для каждого из них. Осталось научиться находить количество искомых пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt;, таких что &amp;lt;tex&amp;gt;i &amp;lt; \frac{n}{2}, j \geqslant \frac{n}{2}&amp;lt;/tex&amp;gt;. Для этого воспользуемся другой известной техникой {{---}} методом двух указателей. Посчитаем массив префиксных сумм для правой половины &amp;lt;tex&amp;gt;pref[i] = \sum_{j=\frac{n}{2}}^{i} a_j&amp;lt;/tex&amp;gt; и суффиксных (&amp;lt;tex&amp;gt;suf[i] = \sum_{j=i}^{\frac{n}{2} + 1} a_j&amp;lt;/tex&amp;gt;) {{---}} для левой. Заведем два указателя (&amp;lt;tex&amp;gt;p_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;p_2&amp;lt;/tex&amp;gt;). Изначально установим &amp;lt;tex&amp;gt;p_1 = \frac{n}{2} - l + 1, p_2 = \frac{n}{2}&amp;lt;/tex&amp;gt;. Пока &amp;lt;tex&amp;gt;p_2 - 1&amp;gt; \frac{n}{2}&amp;lt;/tex&amp;gt; и&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;pref[p_2] + suf[p_1] &amp;gt; W &amp;lt;/tex&amp;gt; будем уменьшать &amp;lt;math&amp;gt;p_2&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Если после этого &amp;lt;math&amp;gt;pref[p_2] + suf[p_1] \leqslant W&amp;lt;/math&amp;gt;, то к ответу прибавим &amp;lt;math&amp;gt;(p_2 - \frac{n}{2} + 1) * (\frac{n}{2} - p_1)&amp;lt;/math&amp;gt;, посго, увеличим &amp;lt;math&amp;gt;p_1&amp;lt;/math&amp;gt; на &amp;lt;math/math&amp;gt;. Так будем делать, пока &amp;lt;math&amp;gt;p_1 &amp;lt; \frac{n}{2}&amp;lt;/math&amp;gt;. В конце сложим текущий ответ и ответы для половин массива {{---}} получим ответ на задачу. Асимптотика такого алгоритма: &amp;lt;tex&amp;gt;T(n) = 2 * T(n / 2) + O(n) = O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вторая задача имеет запросы на изменение и поэтому надо применить динамическую версию divide &amp;amp; conquer {{---}} [[Дерево_отрезков._Построение|дерево отрезков]]. Построим дерево отрезков,&lt;br /&gt;
поддерживающее 2 вида запросов: присвоение в точке и минимум на отрезке. Изначально сделаем так, чтобы дереву отрезков соответствовал массив &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;b_i = 0&amp;lt;/tex&amp;gt;, если в i-м городе принимает госпиталь и &amp;lt;tex&amp;gt;b_i = 1&amp;lt;/tex&amp;gt; иначе. Когда в каком-то городе открывается/закрывается госпиталь {{---}} делаем запрос на изменение в дереве отрезков. Когда требуется узнать ближайщий госпиталь к &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-му городу, можно воспользоваться одной из следующих идей: &lt;br /&gt;
а) (&amp;lt;tex&amp;gt;O(n \cdot log^2(n)&amp;lt;/tex&amp;gt;) Бинарным поиском ищем ближайший слева и ближайший справа к i-му городу госпиталь (такой город &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, что &amp;lt;tex&amp;gt;\min\limits_{k=i..j}b_k= 1&amp;lt;/tex&amp;gt;). Для этого внутри бинарного поиска каждый раз делаем запрос на поиск минимума в дереве отрезков.  &lt;br /&gt;
б) (&amp;lt;tex&amp;gt;O(n \cdot log(n)&amp;lt;/tex&amp;gt;) Будем одним спуском/подъемом по дереву определять, куда нам нужно идти (в левое или правое поддерево), тем самым делая одновоременно и бинарный поиск, и спуск/подъем по дереву.&lt;br /&gt;
&lt;br /&gt;
== Статическая центроидная декомпозиция ==&lt;br /&gt;
Перейдем к обобщению поставленных задач на случай дерева. Начнем, как и полагается, с первой:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть взвешенное дерево &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин, в каждой вершине которого написаны положительные целые числа. Также по-прежнему даны числа &amp;lt;tex&amp;gt;W \geqslant 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; вершин дерева, таких что расстояние между ними не превосходит &amp;lt;math&amp;gt;l&amp;lt;/math&amp;gt; по числу ребер и не превосходит &amp;lt;math&amp;gt;W&amp;lt;/math&amp;gt; по сумме весов.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Для решения новой задачи применим ту же идею, что и была до этого {{---}} разделяй и властвуй. Для этого нам потребуется следующий объект :&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|id=191213&lt;br /&gt;
|definition =&lt;br /&gt;
'''Центроидом дерева''' (англ. ''centroid'') называется такая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, после удаления которой дерево разбивается на несколько (&amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;) поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, таких что для каждого &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;: &amp;lt;tex&amp;gt;|t_i| \leqslant \frac{n}{2}&amp;lt;/tex&amp;gt;, т.е. размер каждого поддерева не превосходит половины размера исходного дерева.&lt;br /&gt;
}}&lt;br /&gt;
Итак, в случае дерева идея разделяй-и-властвуй из предыдущего пункта будет формулироваться так: найдем центроид. Предположим, что мы сумели найти центроид за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; {{---}} размер дерева. Тогда, как и в упрощенной версии задачи {{---}} рекурсивно найдем ответ для всех поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, после чего попытаемся найти недостающие пары вершин, находящиехя в разных поддеревьях и удовлетворяющих вопросу задачи. Для этого будем отвечать на следующие запросы: пусть мы сейчас считаем все пары, где первая из вершин находится в поддереве &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и мы в некоторой структуре данных &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; храним все вершины остальных деревьев (каждую вершину задаем парой &amp;lt;math&amp;gt;(depth(v), length(v))&amp;lt;/math&amp;gt; {{---}} глубина вершины и длина пути до нее из корня поддерева), расстояние до которых от корня их поддерева не превышает &amp;lt;math&amp;gt;min(l, n)&amp;lt;/math&amp;gt;. Тогда просто пройдемся по всем вершинам &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; поддерева &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и прибавим к ответу число вершин в структуре &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt;, таких, что &amp;lt;tex&amp;gt;depth(u) \leqslant l - depth(v)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;length(u) \leqslant l - length(v)&amp;lt;/tex&amp;gt;. Это двумерные запросы, на которые можно отвечать за &amp;lt;math&amp;gt;O(log^2(n)&amp;lt;/math&amp;gt; с помощью [[Многомерное_дерево_отрезков|2d-дерева отрезков]], либо за &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с помощью [[Перечисление_точек_в_произвольном_прямоугольнике_за_n_*_log_%5E(d_-_1)_n_(range_tree)|техники поиска точек в d-мерном пространстве]]. Также читателю предлагается придумать и более эффективные и простые способы решить эту подзадачу.&lt;br /&gt;
&lt;br /&gt;
Оценим итоговую асимптотику: &amp;lt;tex&amp;gt;T(n) = k * T(n / k) + O(n \cdot log(n))&amp;lt;/tex&amp;gt;. Решая это рекурентное соотношение, получим &amp;lt;math&amp;gt;O(n \cdot log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь, как и было обещено, докажем лемму о существовании центроида и опишем алгоритм его эффективного поиска.&lt;br /&gt;
&lt;br /&gt;
=== Лемма о существовании центроида и алгоритм его нахождения. ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
В любом дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; существует центроид.&lt;br /&gt;
|proof=&lt;br /&gt;
Рассмотрим корень дерева &amp;lt;math&amp;gt;(r)&amp;lt;/math&amp;gt;. Положим изначально &amp;lt;math&amp;gt;v = r&amp;lt;/math&amp;gt;. Изначально &amp;lt;math&amp;gt;|subtree(v)| = n&amp;lt;/math&amp;gt;. Среди всех детей &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; выберем вершину &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; с максимальным размером поддерева. Если &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид, то положим &amp;lt;math&amp;gt;v = u&amp;lt;/math&amp;gt; и продолжим выбор нового u, иначе {{---}} остановимся. Докажем, что мы в какой-то момент остановимся. Пусть в призвольный момент времени &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид и размер её наддерева меньше &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, значит максимальное поддерево имеет размер больше чем &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, т.е. &amp;lt;math&amp;gt;|subtree(u)| &amp;gt; \frac{n}{2}&amp;lt;/math&amp;gt;, а значит размер &amp;quot;наддерева&amp;quot; вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; равен &amp;lt;tex&amp;gt;n - |subtree(u)| &amp;lt; \frac{n}{2}&amp;lt;/tex&amp;gt;. При этом теперь размер любого поддерева, на которое распадется дерево t при удалении вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; не превосходит &amp;lt;math&amp;gt;|subtree(u)| - 1&amp;lt;/math&amp;gt;, т.к. наддерево имеет размер меньше, чем поддерево &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, а любое поддерево вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; имеет хотя бы на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; вершину меньше (сама вершина &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;). По индукции получаем, что в любой момент времени размер наддерева вершины v меньше &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, значит мы будем спускаться только вниз по дереву &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, и при переходе к вершине &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} сыну &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; размер максимального поддерева уменьшится как минимум на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Значит не более чем за &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; шагов наши действия прекратятся и мы окажемся в центроиде дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
Итак, мы конструктивно доказали существование центроида и привели линейный относительно размера дерева алгоритм его нахождения. &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Реализация ===&lt;br /&gt;
&lt;br /&gt;
Поиск центроида в дереве:&lt;br /&gt;
&lt;br /&gt;
  '''int''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int''' sz[n])&lt;br /&gt;
     max_subtree = -1&lt;br /&gt;
     '''for''' u : children(v)&lt;br /&gt;
          '''if''' sz[u] &amp;gt; sz[v] / 2 and sz[u] &amp;gt; sz[max_subtree] &amp;lt;font color=green&amp;gt;// считаем sz[-1] = 0 &amp;lt;/font &amp;gt;&lt;br /&gt;
               max_subtree = u&lt;br /&gt;
     '''if''' max_subtree == -1&lt;br /&gt;
          return v&lt;br /&gt;
     '''else''' &lt;br /&gt;
          '''return''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz)&lt;br /&gt;
&lt;br /&gt;
Шаблон решения произвольной задачи на статическую центроидную декомпозицию:&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int''' sz[n])&lt;br /&gt;
     c = &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz) &amp;lt;font color=green&amp;gt;// находим c {{---}} центроид поддерева вершины v &amp;lt;/font &amp;gt;&lt;br /&gt;
     ch = children[c]&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{deleteEdges}&amp;lt;/tex&amp;gt;(c, children) &amp;lt;font color=green&amp;gt;// удаляем все ребра между вершиной с и детьми, чтобы мы не смогли из детей попасть в с. &lt;br /&gt;
         Это полезно делать, если решение подзадачи для поддерева предполагает проход dfs &amp;lt;/font &amp;gt;&lt;br /&gt;
     '''for''' c2 : ch[c]&lt;br /&gt;
          &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;(children, c2, sz)&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{mergeSolution}&amp;lt;/tex&amp;gt;(children, c2) &amp;lt;font color=green&amp;gt;// решаем для текущего поддерева &amp;lt;/font &amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Динамическая центроидная декомпозиция (дерево центроидной декомпозиции) ==&lt;br /&gt;
Теперь вернемся ко второй задаче введения. Для ее решения мы пользовались динамичекой версией divide &amp;amp; conquer {{---}} деревом отрезков. В предыдущем пункте мы определили статический аналог divide &amp;amp; conquer для дерева. Теперь обобщим этот метод для динамических задач.&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Деревом центроидов (или центроидной декомпозицией)''' дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; называют дерево &amp;lt;math&amp;gt;T(t)&amp;lt;/math&amp;gt;, построенное на вершинах дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; следующим образом :&lt;br /&gt;
* Пусть вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} центроид дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Объявим его корнем нового дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пусть при удалении вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; из дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; оно распалось на поддеревья &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, тогда детьми вершины c объявим &amp;lt;tex&amp;gt;T(t_1), T(t_t),\dots, T(t_k)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
=== Свойства центроидной декомпозиции ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
'''Свойства центроидной декомпозиции''' :&lt;br /&gt;
# Глубина дерева центроидов не превосходит &amp;lt;tex&amp;gt;log(n)&amp;lt;/tex&amp;gt;. &lt;br /&gt;
# Каждая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; является центроидом одного из поддеревьев дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждая вершина дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; поддеревьям дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (еще говорят, что вершина принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; центроидам дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, или что эти центроиды содержат вершину)&lt;br /&gt;
# Для любых вершин &amp;lt;tex&amp;gt;u, v \in T (u \neq v)&amp;lt;/tex&amp;gt; верно ровно одно из следующих трех утверждений: a) &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt; b) &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt; c) &amp;lt;tex&amp;gt;T(u) \cap T(v) = \emptyset &amp;lt;/tex&amp;gt;&lt;br /&gt;
# Простой путь между любой парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; содержит центроид &amp;lt;tex&amp;gt;c \in T(t)&amp;lt;/tex&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
# Действительно, т.к. размер поддерева &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; не превосходит &amp;lt;tex&amp;gt;\frac{|subtree(c)|}{2}&amp;lt;/tex&amp;gt;, то при спуске в каждую следующую вершину на пути к любому листу в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; размер поддерева вершины, в которой мы сейчас находимся, уменьшается как минимум на &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;. Значит длина всего пути до листа не превосходит &amp;lt;math&amp;gt;log(n)&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
# Второе свойство очевидно из построения дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, т.к. если вершина принадлежит дереву центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то она является центроидом, а из построения &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы знаем, что каждая вершина исходного дерева принадлежит и дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Третье свойство {{---}} прямое следствие первых двух, т.к. вершина принадлежит любому центроиду &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; т.и т.т., когда c {{---}} отец вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов. Т.к. вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; точно принадлежит дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (свойство 2), то она лежит на каком-то пути в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, причем все ее родители (центроиды) ее содержат. А по свойству 1 длина любого вертикального (и даже простого) пути есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
# Четвертое свойство очевидно из того, что &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; {{---}} дерево. Т.к. &amp;lt;math&amp;gt;T(u)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;T(v)&amp;lt;/math&amp;gt; {{---}} поддеревья различных вершин дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то либо они не пересекаются, либо &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;, либо &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Для доказательства последнего свойства выберем в качестве вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;lca(u, v)&amp;lt;/math&amp;gt; в дереве центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Покажем, что так выбранная вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удовлетворяет заявленным свойствам. То, что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt; {{---}} очевидно по определению &amp;lt;math&amp;gt;lca&amp;lt;/math&amp;gt;, т.к. каждый предок любой вершины в дереве центроидов содержит эту вершину. Теперь докажем, что &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежит на пути между парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt;. Т.к. &amp;lt;math&amp;gt;c = lca(u, v)&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то из &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; нет ребра в такого сына, который содержит одновременно &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в своем поддереве (в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;), значит после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; разделится на несколько поддеревьев, таких что вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; окажутся в разных компонентах связности. А значит найдется такое ребро &amp;lt;math&amp;gt;(c, x)&amp;lt;/math&amp;gt;, которое принадлежало пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, но после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удалилось. Это доказывает то, что вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежала на пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
С помощью описанных свойств дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы можем решить задачу 2 для дерева :&lt;br /&gt;
&lt;br /&gt;
=== Пример решения задачи с помощью центроидной декомпозиции === &lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин. В каждый момент времени любая вершина дерева может быть либо помечена, либо нет. Изначально помечена только вершина номер &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Дан список из &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; запросов: &lt;br /&gt;
* Вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; пометили.&lt;br /&gt;
* С вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; сняли пометку.&lt;br /&gt;
* Найти номер ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
'''Решение :'''&lt;br /&gt;
&lt;br /&gt;
Построим центроидную декомпозицию &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Изначально посчитаем для каждого центроида, содержащего вершину &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt; посчитаем расстояние до вершины &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Для этого воспользуемся [[Метод_двоичного_подъема|методом двоичных подъемов для поиска lca пары вершин в дереве]], а также тем фактом, что если глубина &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве определена как расстояние от корня до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то длина пути между парой вершин &amp;lt;math&amp;gt;(u, v)&amp;lt;/math&amp;gt; есть &amp;lt;tex&amp;gt;len(u, v) = h(u) + h(v) - 2 \cdot h(lca(u, v))&amp;lt;/tex&amp;gt;. Если изначально предпосчитать проходом [[Обход_в_глубину,_цвета_вершин| dfs]] величины &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, то ответ на запрос &amp;lt;math&amp;gt;len(u, v)&amp;lt;/math&amp;gt; можно делать за время &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с &amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt;доп. памятью. Также можно воспользоваться техникой [[Сведение_задачи_LCA_к_задаче_RMQ|сведения задачи LCA к RMQ]] и решить с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; времени на запрос. Теперь научимся отвечать на запросы. Из последнего свойства центроидной декомпозиции видно, что если &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} искомая ближайшая помеченная вершина к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то путь между ними содержит центроид &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;, причем &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок одновременно вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве&amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Поэтому заведем [[Красно-черное_дерево|двоичное дерево поиска]] для каждого центроида &amp;lt;math&amp;gt;c \in T&amp;lt;/math&amp;gt;. В этой структуре для каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; будем хранить пары &amp;lt;math&amp;gt;(len(c, u), u)&amp;lt;/math&amp;gt; для всех помеченных вершин &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в поддереве центроидов &amp;lt;math&amp;gt;T(c)&amp;lt;/math&amp;gt;. Когда приходит запрос пометить вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} добавим в структуру данных для всех предков &amp;lt;math&amp;gt;p_c&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; пары &amp;lt;math&amp;gt;(len(p_c, v), v)&amp;lt;/math&amp;gt;. Мы совершим &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; добавлений, затратив &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; действий на каждое. Запрос снятия пометки с вершины обрабатывается аналогичными удалениями. Запрос поиска ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины {{---}} это запрос поиска вершины u, такой что  величина &amp;lt;math&amp;gt;len(c, u) + len(c, v)&amp;lt;/math&amp;gt; минимальна, где &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов (по пятому свойству, нас интересуют именно такие &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;). Этот запрос занимает так же &amp;lt;tex&amp;gt;O(log^2(n))&amp;lt;/tex&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
Итак, мы научились решать задачу с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и временной сложностью &amp;lt;math&amp;gt;O(log^2(n))&amp;lt;/math&amp;gt; на запрос любого типа с предварительным предпосчетом за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Варианты хранения центроидной декомпозиции ==&lt;br /&gt;
Для хранения дерева центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; есть &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; подхода, имеющих свои преимущества и недостатки :&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева запомним величину &amp;lt;math&amp;gt;p_v&amp;lt;/math&amp;gt; {{---}} номер предка вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Этот подход наиболее экономный по памяти (&amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;), но уступает в скорости и функциональности.&lt;br /&gt;
&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева будем хранить весь массив предков &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt; в дереве центроидов.&lt;br /&gt;
&lt;br /&gt;
Этот подход уступает в количестве необходимой дополнительной памяти (&amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt; суммарно), но имеет ряд преимуществ :&lt;br /&gt;
&lt;br /&gt;
#При проходе по массиву предков фиксированной вершины будет выигрыш в скорости работы, т.к. весь массив будет лежать непрерывным блоком данных и следовательно будет закэширован&lt;br /&gt;
# На массиве предков можно строить различные структуры данных (такие как, например, дерево отрезков) для быстрого (в случае с деревом отрезков &amp;lt;math&amp;gt;O(log(log(n)))&amp;lt;/math&amp;gt; на запрос) поиска предка с необходимыми свойствами. Так, например, в описанной выше задаче про помеченные вершины наибольшего общего предка можно искать методом двоичных подъемов за &amp;lt;math&amp;gt;O(log(log(n))&amp;lt;/math&amp;gt; на запрос, т.к. размер массива предков есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; (по свойству 1 центроидной декомпозиции). Используя эту оптимизацию можно получить время &amp;lt;math&amp;gt;O(log(n)) + O(log(log(n)) = O(log(n))&amp;lt;/math&amp;gt; на запрос нахождения ближайшей помеченной вершины. Чтобы добиться улучшенной асимптотики для запросов изменения можно хранить дерево отрезков на каждом из путей &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt;, в каждой вершине которого хранить двоичное дерево поиска и поддерживать отложенные операции. Тогда ответ на эти запросы будет занимать &amp;lt;math&amp;gt;O(log(n) \cdot log(log(n))&amp;lt;/math&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
&lt;br /&gt;
*[[:Дерево_отрезков._Построение|Дерево отрезков]]&lt;br /&gt;
*[[:Heavy-light_декомпозиция|Heavy-light декомпозиция]]&lt;br /&gt;
*[[:Метод_двоичного_подъема| Метод двоичного подъема]]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы и структуры данных]]&lt;/div&gt;</summary>
		<author><name>92.255.113.52</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61636</id>
		<title>Centroid decomposition</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61636"/>
				<updated>2017-06-14T21:52:03Z</updated>
		
		<summary type="html">&lt;p&gt;92.255.113.52: /* Свойства центроидной декомпозиции */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Centroid decomposition (рус. центроидная декомпозиция) {{---}} это структура данных, позволяющая отвечать на запросы на дереве. Чаще всего это запросы, связанные с нахождением функции на вершинах, связанных неравенством на расстояние между ними в дереве. Также иногда применяется для запросов на путях в дереве.&lt;br /&gt;
&lt;br /&gt;
== Введение ==&lt;br /&gt;
Рассмотрим &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; задачи на обычном массиве (в дальнейшем мы будем их обобщать на случай дерева):&lt;br /&gt;
&lt;br /&gt;
Задача &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть массив &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; положительных целых чисел из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов. Также дано число &amp;lt;tex&amp;gt;W \geqslant 0&amp;lt;/tex&amp;gt; и число &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; индексов массива, таких что &amp;lt;tex&amp;gt;|j - i| \leqslant l &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\sum_{k=i}^{j} a_k \leqslant W&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
Задача &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть прямая дорога, на которой расположены &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; городов. В некоторых городах есть госпитали, которые могут принимать больных. Поступают запросы вида :&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;, в котором находится больной и требуется найти такой город &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;|u - v|&amp;lt;/tex&amp;gt; минимально возможное.&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что больше он не будет принимать больных&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что теперь он может принимать больных&lt;br /&gt;
}}&lt;br /&gt;
Для начала решим обе задачи. &lt;br /&gt;
Первая задача решается методом [[Сортировка_слиянием|divide &amp;amp; conquer (рус. разделяй и властвуй)]] {{---}} давайте разделим массив &amp;lt;tex&amp;gt;a[0 \dots n-1]&amp;lt;/tex&amp;gt; на 2 массива &amp;lt;tex&amp;gt;a[0\dots\frac{n}{2} - 1]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;a[\frac{n}{2} \dots n-1]&amp;lt;/tex&amp;gt; и рекурсивно решим задачу для каждого из них. Осталось научиться находить количество искомых пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt;, таких что &amp;lt;tex&amp;gt;i &amp;lt; \frac{n}{2}, j \geqslant \frac{n}{2}&amp;lt;/tex&amp;gt;. Для этого воспользуемся другой известной техникой {{---}} методом двух указателей. Посчитаем массив префиксных сумм для правой половины &amp;lt;tex&amp;gt;pref[i] = \sum_{j=\frac{n}{2}}^{i} a_j&amp;lt;/tex&amp;gt; и суффиксных (&amp;lt;tex&amp;gt;suf[i] = \sum_{j=i}^{\frac{n}{2} + 1} a_j&amp;lt;/tex&amp;gt;) {{---}} для левой. Заведем два указателя (&amp;lt;tex&amp;gt;p_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;p_2&amp;lt;/tex&amp;gt;). Изначально установим &amp;lt;tex&amp;gt;p_1 = \frac{n}{2} - l + 1, p_2 = \frac{n}{2}&amp;lt;/tex&amp;gt;. Пока &amp;lt;tex&amp;gt;p_2 - 1&amp;gt; \frac{n}{2}&amp;lt;/tex&amp;gt; и&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;pref[p_2] + suf[p_1] &amp;gt; W &amp;lt;/tex&amp;gt; будем уменьшать &amp;lt;math&amp;gt;p_2&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Если после этого &amp;lt;math&amp;gt;pref[p_2] + suf[p_1] \leqslant W&amp;lt;/math&amp;gt;, то к ответу прибавим &amp;lt;math&amp;gt;(p_2 - \frac{n}{2} + 1) * (\frac{n}{2} - p_1)&amp;lt;/math&amp;gt;, посго, увеличим &amp;lt;math&amp;gt;p_1&amp;lt;/math&amp;gt; на &amp;lt;math/math&amp;gt;. Так будем делать, пока &amp;lt;math&amp;gt;p_1 &amp;lt; \frac{n}{2}&amp;lt;/math&amp;gt;. В конце сложим текущий ответ и ответы для половин массива {{---}} получим ответ на задачу. Асимптотика такого алгоритма: &amp;lt;tex&amp;gt;T(n) = 2 * T(n / 2) + O(n) = O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вторая задача имеет запросы на изменение и поэтому надо применить динамическую версию divide &amp;amp; conquer {{---}} [[Дерево_отрезков._Построение|дерево отрезков]]. Построим дерево отрезков,&lt;br /&gt;
поддерживающее 2 вида запросов: присвоение в точке и минимум на отрезке. Изначально сделаем так, чтобы дереву отрезков соответствовал массив &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;b_i = 0&amp;lt;/tex&amp;gt;, если в i-м городе принимает госпиталь и &amp;lt;tex&amp;gt;b_i = 1&amp;lt;/tex&amp;gt; иначе. Когда в каком-то городе открывается/закрывается госпиталь {{---}} делаем запрос на изменение в дереве отрезков. Когда требуется узнать ближайщий госпиталь к &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-му городу, можно воспользоваться одной из следующих идей: &lt;br /&gt;
а) (&amp;lt;tex&amp;gt;O(n \cdot log^2(n)&amp;lt;/tex&amp;gt;) Бинарным поиском ищем ближайший слева и ближайший справа к i-му городу госпиталь (такой город &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, что &amp;lt;tex&amp;gt;\min\limits_{k=i..j}b_k= 1&amp;lt;/tex&amp;gt;). Для этого внутри бинарного поиска каждый раз делаем запрос на поиск минимума в дереве отрезков.  &lt;br /&gt;
б) (&amp;lt;tex&amp;gt;O(n \cdot log(n)&amp;lt;/tex&amp;gt;) Будем одним спуском/подъемом по дереву определять, куда нам нужно идти (в левое или правое поддерево), тем самым делая одновоременно и бинарный поиск, и спуск/подъем по дереву.&lt;br /&gt;
&lt;br /&gt;
== Статическая центроидная декомпозиция ==&lt;br /&gt;
Перейдем к обобщению поставленных задач на случай дерева. Начнем, как и полагается, с первой:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть взвешенное дерево &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин, в каждой вершине которого написаны положительные целые числа. Также по-прежнему даны числа &amp;lt;tex&amp;gt;W &amp;gt;= 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; вершин дерева, таких что расстояние между ними не превосходит &amp;lt;math&amp;gt;l&amp;lt;/math&amp;gt; по числу ребер и не превосходит &amp;lt;math&amp;gt;W&amp;lt;/math&amp;gt; по сумме весов.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Для решения новой задачи применим ту же идею, что и была до этого {{---}} разделяй и властвуй. Для этого нам потребуется следующий объект :&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|id=191213&lt;br /&gt;
|definition =&lt;br /&gt;
'''Центроидом дерева''' (англ. ''centroid'') называется такая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, после удаления которой дерево разбивается на несколько (&amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;) поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, таких что для каждого &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;: &amp;lt;tex&amp;gt;|t_i| \leqslant \frac{n}{2}&amp;lt;/tex&amp;gt;, т.е. размер каждого поддерева не превосходит половины размера исходного дерева.&lt;br /&gt;
}}&lt;br /&gt;
Итак, в случае дерева идея разделяй-и-властвуй из предыдущего пункта будет формулироваться так: найдем центроид. Предположим, что мы сумели найти центроид за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; {{---}} размер дерева. Тогда, как и в упрощенной версии задачи {{---}} рекурсивно найдем ответ для всех поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, после чего попытаемся найти недостающие пары вершин, находящиехя в разных поддеревьях и удовлетворяющих вопросу задачи. Для этого будем отвечать на следующие запросы: пусть мы сейчас считаем все пары, где первая из вершин находится в поддереве &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и мы в некоторой структуре данных &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; храним все вершины остальных деревьев (каждую вершину задаем парой &amp;lt;math&amp;gt;(depth(v), length(v))&amp;lt;/math&amp;gt; {{---}} глубина вершины и длина пути до нее из корня поддерева), расстояние до которых от корня их поддерева не превышает &amp;lt;math&amp;gt;min(l, n)&amp;lt;/math&amp;gt;. Тогда просто пройдемся по всем вершинам &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; поддерева &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и прибавим к ответу число вершин в структуре &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt;, таких, что &amp;lt;tex&amp;gt;depth(u) \leqslant l - depth(v)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;length(u) \leqslant l - length(v)&amp;lt;/tex&amp;gt;. Это двумерные запросы, на которые можно отвечать за &amp;lt;math&amp;gt;O(log^2(n)&amp;lt;/math&amp;gt; с помощью [[Многомерное_дерево_отрезков|2d-дерева отрезков]], либо за &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с помощью [[Перечисление_точек_в_произвольном_прямоугольнике_за_n_*_log_%5E(d_-_1)_n_(range_tree)|техники поиска точек в d-мерном пространстве]]. Также читателю предлагается придумать и более эффективные и простые способы решить эту подзадачу.&lt;br /&gt;
&lt;br /&gt;
Оценим итоговую асимптотику: &amp;lt;tex&amp;gt;T(n) = k * T(n / k) + O(n \cdot log(n))&amp;lt;/tex&amp;gt;. Решая это рекурентное соотношение, получим &amp;lt;math&amp;gt;O(n \cdot log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь, как и было обещено, докажем лемму о существовании центроида и опишем алгоритм его эффективного поиска.&lt;br /&gt;
&lt;br /&gt;
=== Лемма о существовании центроида и алгоритм его нахождения. ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
В любом дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; существует центроид.&lt;br /&gt;
|proof=&lt;br /&gt;
Рассмотрим корень дерева &amp;lt;math&amp;gt;(r)&amp;lt;/math&amp;gt;. Положим изначально &amp;lt;math&amp;gt;v = r&amp;lt;/math&amp;gt;. Изначально &amp;lt;math&amp;gt;|subtree(v)| = n&amp;lt;/math&amp;gt;. Среди всех детей &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; выберем вершину &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; с максимальным размером поддерева. Если &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид, то положим &amp;lt;math&amp;gt;v = u&amp;lt;/math&amp;gt; и продолжим выбор нового u, иначе {{---}} остановимся. Докажем, что мы в какой-то момент остановимся. Пусть в призвольный момент времени &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид и размер её наддерева меньше &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, значит максимальное поддерево имеет размер больше чем &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, т.е. &amp;lt;math&amp;gt;|subtree(u)| &amp;gt; \frac{n}{2}&amp;lt;/math&amp;gt;, а значит размер &amp;quot;наддерева&amp;quot; вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; равен &amp;lt;tex&amp;gt;n - |subtree(u)| &amp;lt; \frac{n}{2}&amp;lt;/tex&amp;gt;. При этом теперь размер любого поддерева, на которое распадется дерево t при удалении вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; не превосходит &amp;lt;math&amp;gt;|subtree(u)| - 1&amp;lt;/math&amp;gt;, т.к. наддерево имеет размер меньше, чем поддерево &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, а любое поддерево вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; имеет хотя бы на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; вершину меньше (сама вершина &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;). По индукции получаем, что в любой момент времени размер наддерева вершины v меньше &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, значит мы будем спускаться только вниз по дереву &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, и при переходе к вершине &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} сыну &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; размер максимального поддерева уменьшится как минимум на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Значит не более чем за &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; шагов наши действия прекратятся и мы окажемся в центроиде дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
Итак, мы конструктивно доказали существование центроида и привели линейный относительно размера дерева алгоритм его нахождения. &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Реализация ===&lt;br /&gt;
&lt;br /&gt;
Поиск центроида в дереве:&lt;br /&gt;
&lt;br /&gt;
  '''int''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int''' sz[n])&lt;br /&gt;
     max_subtree = -1&lt;br /&gt;
     '''for''' u : children(v)&lt;br /&gt;
          '''if''' sz[u] &amp;gt; sz[v] / 2 and sz[u] &amp;gt; sz[max_subtree] &amp;lt;font color=green&amp;gt;// считаем sz[-1] = 0 &amp;lt;/font &amp;gt;&lt;br /&gt;
               max_subtree = u&lt;br /&gt;
     '''if''' max_subtree == -1&lt;br /&gt;
          return v&lt;br /&gt;
     '''else''' &lt;br /&gt;
          '''return''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz)&lt;br /&gt;
&lt;br /&gt;
Шаблон решения произвольной задачи на статическую центроидную декомпозицию:&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int''' sz[n])&lt;br /&gt;
     c = &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz) &amp;lt;font color=green&amp;gt;// находим c {{---}} центроид поддерева вершины v &amp;lt;/font &amp;gt;&lt;br /&gt;
     ch = children[c]&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{deleteEdges}&amp;lt;/tex&amp;gt;(c, children) &amp;lt;font color=green&amp;gt;// удаляем все ребра между вершиной с и детьми, чтобы мы не смогли из детей попасть в с. &lt;br /&gt;
         Это полезно делать, если решение подзадачи для поддерева предполагает проход dfs &amp;lt;/font &amp;gt;&lt;br /&gt;
     '''for''' c2 : ch[c]&lt;br /&gt;
          &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;(children, c2, sz)&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{mergeSolution}&amp;lt;/tex&amp;gt;(children, c2) &amp;lt;font color=green&amp;gt;// решаем для текущего поддерева &amp;lt;/font &amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Динамическая центроидная декомпозиция (дерево центроидной декомпозиции) ==&lt;br /&gt;
Теперь вернемся ко второй задаче введения. Для ее решения мы пользовались динамичекой версией divide &amp;amp; conquer {{---}} деревом отрезков. В предыдущем пункте мы определили статический аналог divide &amp;amp; conquer для дерева. Теперь обобщим этот метод для динамических задач.&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Деревом центроидов (или центроидной декомпозицией)''' дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; называют дерево &amp;lt;math&amp;gt;T(t)&amp;lt;/math&amp;gt;, построенное на вершинах дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; следующим образом :&lt;br /&gt;
* Пусть вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} центроид дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Объявим его корнем нового дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пусть при удалении вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; из дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; оно распалось на поддеревья &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, тогда детьми вершины c объявим &amp;lt;tex&amp;gt;T(t_1), T(t_t),\dots, T(t_k)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
=== Свойства центроидной декомпозиции ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
'''Свойства центроидной декомпозиции''' :&lt;br /&gt;
# Глубина дерева центроидов не превосходит &amp;lt;tex&amp;gt;log(n)&amp;lt;/tex&amp;gt;. &lt;br /&gt;
# Каждая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; является центроидом одного из поддеревьев дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждая вершина дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; поддеревьям дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (еще говорят, что вершина принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; центроидам дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, или что эти центроиды содержат вершину)&lt;br /&gt;
# Для любых вершин &amp;lt;tex&amp;gt;u, v \in T (u \neq v)&amp;lt;/tex&amp;gt; верно ровно одно из следующих трех утверждений: a) &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt; b) &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt; c) &amp;lt;tex&amp;gt;T(u) \cap T(v) = \emptyset &amp;lt;/tex&amp;gt;&lt;br /&gt;
# Простой путь между любой парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; содержит центроид &amp;lt;tex&amp;gt;c \in T(t)&amp;lt;/tex&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
# Действительно, т.к. размер поддерева &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; не превосходит &amp;lt;tex&amp;gt;\frac{|subtree(c)|}{2}&amp;lt;/tex&amp;gt;, то при спуске в каждую следующую вершину на пути к любому листу в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; размер поддерева вершины, в которой мы сейчас находимся, уменьшается как минимум на &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;. Значит длина всего пути до листа не превосходит &amp;lt;math&amp;gt;log(n)&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
# Второе свойство очевидно из построения дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, т.к. если вершина принадлежит дереву центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то она является центроидом, а из построения &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы знаем, что каждая вершина исходного дерева принадлежит и дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Третье свойство {{---}} прямое следствие первых двух, т.к. вершина принадлежит любому центроиду &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; т.и т.т., когда c {{---}} отец вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов. Т.к. вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; точно принадлежит дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (свойство 2), то она лежит на каком-то пути в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, причем все ее родители (центроиды) ее содержат. А по свойству 1 длина любого вертикального (и даже простого) пути есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
# Четвертое свойство очевидно из того, что &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; {{---}} дерево. Т.к. &amp;lt;math&amp;gt;T(u)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;T(v)&amp;lt;/math&amp;gt; {{---}} поддеревья различных вершин дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то либо они не пересекаются, либо &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;, либо &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Для доказательства последнего свойства выберем в качестве вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;lca(u, v)&amp;lt;/math&amp;gt; в дереве центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Покажем, что так выбранная вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удовлетворяет заявленным свойствам. То, что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt; {{---}} очевидно по определению &amp;lt;math&amp;gt;lca&amp;lt;/math&amp;gt;, т.к. каждый предок любой вершины в дереве центроидов содержит эту вершину. Теперь докажем, что &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежит на пути между парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt;. Т.к. &amp;lt;math&amp;gt;c = lca(u, v)&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то из &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; нет ребра в такого сына, который содержит одновременно &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в своем поддереве (в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;), значит после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; разделится на несколько поддеревьев, таких что вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; окажутся в разных компонентах связности. А значит найдется такое ребро &amp;lt;math&amp;gt;(c, x)&amp;lt;/math&amp;gt;, которое принадлежало пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, но после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удалилось. Это доказывает то, что вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежала на пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
С помощью описанных свойств дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы можем решить задачу 2 для дерева :&lt;br /&gt;
&lt;br /&gt;
=== Пример решения задачи с помощью центроидной декомпозиции === &lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин. В каждый момент времени любая вершина дерева может быть либо помечена, либо нет. Изначально помечена только вершина номер &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Дан список из &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; запросов: &lt;br /&gt;
* Вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; пометили.&lt;br /&gt;
* С вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; сняли пометку.&lt;br /&gt;
* Найти номер ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
'''Решение :'''&lt;br /&gt;
&lt;br /&gt;
Построим центроидную декомпозицию &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Изначально посчитаем для каждого центроида, содержащего вершину &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt; посчитаем расстояние до вершины &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Для этого воспользуемся [[Метод_двоичного_подъема|методом двоичных подъемов для поиска lca пары вершин в дереве]], а также тем фактом, что если глубина &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве определена как расстояние от корня до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то длина пути между парой вершин &amp;lt;math&amp;gt;(u, v)&amp;lt;/math&amp;gt; есть &amp;lt;tex&amp;gt;len(u, v) = h(u) + h(v) - 2 \cdot h(lca(u, v))&amp;lt;/tex&amp;gt;. Если изначально предпосчитать проходом [[Обход_в_глубину,_цвета_вершин| dfs]] величины &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, то ответ на запрос &amp;lt;math&amp;gt;len(u, v)&amp;lt;/math&amp;gt; можно делать за время &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с &amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt;доп. памятью. Также можно воспользоваться техникой [[Сведение_задачи_LCA_к_задаче_RMQ|сведения задачи LCA к RMQ]] и решить с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; времени на запрос. Теперь научимся отвечать на запросы. Из последнего свойства центроидной декомпозиции видно, что если &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} искомая ближайшая помеченная вершина к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то путь между ними содержит центроид &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;, причем &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок одновременно вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве&amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Поэтому заведем [[Красно-черное_дерево|двоичное дерево поиска]] для каждого центроида &amp;lt;math&amp;gt;c \in T&amp;lt;/math&amp;gt;. В этой структуре для каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; будем хранить пары &amp;lt;math&amp;gt;(len(c, u), u)&amp;lt;/math&amp;gt; для всех помеченных вершин &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в поддереве центроидов &amp;lt;math&amp;gt;T(c)&amp;lt;/math&amp;gt;. Когда приходит запрос пометить вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} добавим в структуру данных для всех предков &amp;lt;math&amp;gt;p_c&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; пары &amp;lt;math&amp;gt;(len(p_c, v), v)&amp;lt;/math&amp;gt;. Мы совершим &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; добавлений, затратив &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; действий на каждое. Запрос снятия пометки с вершины обрабатывается аналогичными удалениями. Запрос поиска ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины {{---}} это запрос поиска вершины u, такой что  величина &amp;lt;math&amp;gt;len(c, u) + len(c, v)&amp;lt;/math&amp;gt; минимальна, где &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов (по пятому свойству, нас интересуют именно такие &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;). Этот запрос занимает так же &amp;lt;tex&amp;gt;O(log^2(n))&amp;lt;/tex&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
Итак, мы научились решать задачу с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и временной сложностью &amp;lt;math&amp;gt;O(log^2(n))&amp;lt;/math&amp;gt; на запрос любого типа с предварительным предпосчетом за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Варианты хранения центроидной декомпозиции ==&lt;br /&gt;
Для хранения дерева центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; есть &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; подхода, имеющих свои преимущества и недостатки :&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева запомним величину &amp;lt;math&amp;gt;p_v&amp;lt;/math&amp;gt; {{---}} номер предка вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Этот подход наиболее экономный по памяти (&amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;), но уступает в скорости и функциональности.&lt;br /&gt;
&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева будем хранить весь массив предков &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt; в дереве центроидов.&lt;br /&gt;
&lt;br /&gt;
Этот подход уступает в количестве необходимой дополнительной памяти (&amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt; суммарно), но имеет ряд преимуществ :&lt;br /&gt;
&lt;br /&gt;
#При проходе по массиву предков фиксированной вершины будет выигрыш в скорости работы, т.к. весь массив будет лежать непрерывным блоком данных и следовательно будет закэширован&lt;br /&gt;
# На массиве предков можно строить различные структуры данных (такие как, например, дерево отрезков) для быстрого (в случае с деревом отрезков &amp;lt;math&amp;gt;O(log(log(n)))&amp;lt;/math&amp;gt; на запрос) поиска предка с необходимыми свойствами. Так, например, в описанной выше задаче про помеченные вершины наибольшего общего предка можно искать методом двоичных подъемов за &amp;lt;math&amp;gt;O(log(log(n))&amp;lt;/math&amp;gt; на запрос, т.к. размер массива предков есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; (по свойству 1 центроидной декомпозиции). Используя эту оптимизацию можно получить время &amp;lt;math&amp;gt;O(log(n)) + O(log(log(n)) = O(log(n))&amp;lt;/math&amp;gt; на запрос нахождения ближайшей помеченной вершины. Чтобы добиться улучшенной асимптотики для запросов изменения можно хранить дерево отрезков на каждом из путей &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt;, в каждой вершине которого хранить двоичное дерево поиска и поддерживать отложенные операции. Тогда ответ на эти запросы будет занимать &amp;lt;math&amp;gt;O(log(n) \cdot log(log(n))&amp;lt;/math&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
&lt;br /&gt;
*[[:Дерево_отрезков._Построение|Дерево отрезков]]&lt;br /&gt;
*[[:Heavy-light_декомпозиция|Heavy-light декомпозиция]]&lt;br /&gt;
*[[:Метод_двоичного_подъема| Метод двоичного подъема]]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы и структуры данных]]&lt;/div&gt;</summary>
		<author><name>92.255.113.52</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61635</id>
		<title>Centroid decomposition</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61635"/>
				<updated>2017-06-14T21:45:34Z</updated>
		
		<summary type="html">&lt;p&gt;92.255.113.52: /* Введение */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Centroid decomposition (рус. центроидная декомпозиция) {{---}} это структура данных, позволяющая отвечать на запросы на дереве. Чаще всего это запросы, связанные с нахождением функции на вершинах, связанных неравенством на расстояние между ними в дереве. Также иногда применяется для запросов на путях в дереве.&lt;br /&gt;
&lt;br /&gt;
== Введение ==&lt;br /&gt;
Рассмотрим &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; задачи на обычном массиве (в дальнейшем мы будем их обобщать на случай дерева):&lt;br /&gt;
&lt;br /&gt;
Задача &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть массив &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; положительных целых чисел из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов. Также дано число &amp;lt;tex&amp;gt;W \geqslant 0&amp;lt;/tex&amp;gt; и число &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; индексов массива, таких что &amp;lt;tex&amp;gt;|j - i| \leqslant l &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\sum_{k=i}^{j} a_k \leqslant W&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
Задача &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть прямая дорога, на которой расположены &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; городов. В некоторых городах есть госпитали, которые могут принимать больных. Поступают запросы вида :&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;, в котором находится больной и требуется найти такой город &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;|u - v|&amp;lt;/tex&amp;gt; минимально возможное.&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что больше он не будет принимать больных&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что теперь он может принимать больных&lt;br /&gt;
}}&lt;br /&gt;
Для начала решим обе задачи. &lt;br /&gt;
Первая задача решается методом [[Сортировка_слиянием|divide &amp;amp; conquer (рус. разделяй и властвуй)]] {{---}} давайте разделим массив &amp;lt;tex&amp;gt;a[0 \dots n-1]&amp;lt;/tex&amp;gt; на 2 массива &amp;lt;tex&amp;gt;a[0\dots\frac{n}{2} - 1]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;a[\frac{n}{2} \dots n-1]&amp;lt;/tex&amp;gt; и рекурсивно решим задачу для каждого из них. Осталось научиться находить количество искомых пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt;, таких что &amp;lt;tex&amp;gt;i &amp;lt; \frac{n}{2}, j \geqslant \frac{n}{2}&amp;lt;/tex&amp;gt;. Для этого воспользуемся другой известной техникой {{---}} методом двух указателей. Посчитаем массив префиксных сумм для правой половины &amp;lt;tex&amp;gt;pref[i] = \sum_{j=\frac{n}{2}}^{i} a_j&amp;lt;/tex&amp;gt; и суффиксных (&amp;lt;tex&amp;gt;suf[i] = \sum_{j=i}^{\frac{n}{2} + 1} a_j&amp;lt;/tex&amp;gt;) {{---}} для левой. Заведем два указателя (&amp;lt;tex&amp;gt;p_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;p_2&amp;lt;/tex&amp;gt;). Изначально установим &amp;lt;tex&amp;gt;p_1 = \frac{n}{2} - l + 1, p_2 = \frac{n}{2}&amp;lt;/tex&amp;gt;. Пока &amp;lt;tex&amp;gt;p_2 - 1&amp;gt; \frac{n}{2}&amp;lt;/tex&amp;gt; и&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;pref[p_2] + suf[p_1] &amp;gt; W &amp;lt;/tex&amp;gt; будем уменьшать &amp;lt;math&amp;gt;p_2&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Если после этого &amp;lt;math&amp;gt;pref[p_2] + suf[p_1] \leqslant W&amp;lt;/math&amp;gt;, то к ответу прибавим &amp;lt;math&amp;gt;(p_2 - \frac{n}{2} + 1) * (\frac{n}{2} - p_1)&amp;lt;/math&amp;gt;, посго, увеличим &amp;lt;math&amp;gt;p_1&amp;lt;/math&amp;gt; на &amp;lt;math/math&amp;gt;. Так будем делать, пока &amp;lt;math&amp;gt;p_1 &amp;lt; \frac{n}{2}&amp;lt;/math&amp;gt;. В конце сложим текущий ответ и ответы для половин массива {{---}} получим ответ на задачу. Асимптотика такого алгоритма: &amp;lt;tex&amp;gt;T(n) = 2 * T(n / 2) + O(n) = O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вторая задача имеет запросы на изменение и поэтому надо применить динамическую версию divide &amp;amp; conquer {{---}} [[Дерево_отрезков._Построение|дерево отрезков]]. Построим дерево отрезков,&lt;br /&gt;
поддерживающее 2 вида запросов: присвоение в точке и минимум на отрезке. Изначально сделаем так, чтобы дереву отрезков соответствовал массив &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;b_i = 0&amp;lt;/tex&amp;gt;, если в i-м городе принимает госпиталь и &amp;lt;tex&amp;gt;b_i = 1&amp;lt;/tex&amp;gt; иначе. Когда в каком-то городе открывается/закрывается госпиталь {{---}} делаем запрос на изменение в дереве отрезков. Когда требуется узнать ближайщий госпиталь к &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-му городу, можно воспользоваться одной из следующих идей: &lt;br /&gt;
а) (&amp;lt;tex&amp;gt;O(n \cdot log^2(n)&amp;lt;/tex&amp;gt;) Бинарным поиском ищем ближайший слева и ближайший справа к i-му городу госпиталь (такой город &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, что &amp;lt;tex&amp;gt;\min\limits_{k=i..j}b_k= 1&amp;lt;/tex&amp;gt;). Для этого внутри бинарного поиска каждый раз делаем запрос на поиск минимума в дереве отрезков.  &lt;br /&gt;
б) (&amp;lt;tex&amp;gt;O(n \cdot log(n)&amp;lt;/tex&amp;gt;) Будем одним спуском/подъемом по дереву определять, куда нам нужно идти (в левое или правое поддерево), тем самым делая одновоременно и бинарный поиск, и спуск/подъем по дереву.&lt;br /&gt;
&lt;br /&gt;
== Статическая центроидная декомпозиция ==&lt;br /&gt;
Перейдем к обобщению поставленных задач на случай дерева. Начнем, как и полагается, с первой:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть взвешенное дерево &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин, в каждой вершине которого написаны положительные целые числа. Также по-прежнему даны числа &amp;lt;tex&amp;gt;W &amp;gt;= 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; вершин дерева, таких что расстояние между ними не превосходит &amp;lt;math&amp;gt;l&amp;lt;/math&amp;gt; по числу ребер и не превосходит &amp;lt;math&amp;gt;W&amp;lt;/math&amp;gt; по сумме весов.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Для решения новой задачи применим ту же идею, что и была до этого {{---}} разделяй и властвуй. Для этого нам потребуется следующий объект :&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|id=191213&lt;br /&gt;
|definition =&lt;br /&gt;
'''Центроидом дерева''' (англ. ''centroid'') называется такая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, после удаления которой дерево разбивается на несколько (&amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;) поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, таких что для каждого &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;: &amp;lt;tex&amp;gt;|t_i| \leqslant \frac{n}{2}&amp;lt;/tex&amp;gt;, т.е. размер каждого поддерева не превосходит половины размера исходного дерева.&lt;br /&gt;
}}&lt;br /&gt;
Итак, в случае дерева идея разделяй-и-властвуй из предыдущего пункта будет формулироваться так: найдем центроид. Предположим, что мы сумели найти центроид за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; {{---}} размер дерева. Тогда, как и в упрощенной версии задачи {{---}} рекурсивно найдем ответ для всех поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, после чего попытаемся найти недостающие пары вершин, находящиехя в разных поддеревьях и удовлетворяющих вопросу задачи. Для этого будем отвечать на следующие запросы: пусть мы сейчас считаем все пары, где первая из вершин находится в поддереве &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и мы в некоторой структуре данных &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; храним все вершины остальных деревьев (каждую вершину задаем парой &amp;lt;math&amp;gt;(depth(v), length(v))&amp;lt;/math&amp;gt; {{---}} глубина вершины и длина пути до нее из корня поддерева), расстояние до которых от корня их поддерева не превышает &amp;lt;math&amp;gt;min(l, n)&amp;lt;/math&amp;gt;. Тогда просто пройдемся по всем вершинам &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; поддерева &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и прибавим к ответу число вершин в структуре &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt;, таких, что &amp;lt;tex&amp;gt;depth(u) \leqslant l - depth(v)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;length(u) \leqslant l - length(v)&amp;lt;/tex&amp;gt;. Это двумерные запросы, на которые можно отвечать за &amp;lt;math&amp;gt;O(log^2(n)&amp;lt;/math&amp;gt; с помощью [[Многомерное_дерево_отрезков|2d-дерева отрезков]], либо за &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с помощью [[Перечисление_точек_в_произвольном_прямоугольнике_за_n_*_log_%5E(d_-_1)_n_(range_tree)|техники поиска точек в d-мерном пространстве]]. Также читателю предлагается придумать и более эффективные и простые способы решить эту подзадачу.&lt;br /&gt;
&lt;br /&gt;
Оценим итоговую асимптотику: &amp;lt;tex&amp;gt;T(n) = k * T(n / k) + O(n \cdot log(n))&amp;lt;/tex&amp;gt;. Решая это рекурентное соотношение, получим &amp;lt;math&amp;gt;O(n \cdot log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь, как и было обещено, докажем лемму о существовании центроида и опишем алгоритм его эффективного поиска.&lt;br /&gt;
&lt;br /&gt;
=== Лемма о существовании центроида и алгоритм его нахождения. ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
В любом дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; существует центроид.&lt;br /&gt;
|proof=&lt;br /&gt;
Рассмотрим корень дерева &amp;lt;math&amp;gt;(r)&amp;lt;/math&amp;gt;. Положим изначально &amp;lt;math&amp;gt;v = r&amp;lt;/math&amp;gt;. Изначально &amp;lt;math&amp;gt;|subtree(v)| = n&amp;lt;/math&amp;gt;. Среди всех детей &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; выберем вершину &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; с максимальным размером поддерева. Если &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид, то положим &amp;lt;math&amp;gt;v = u&amp;lt;/math&amp;gt; и продолжим выбор нового u, иначе {{---}} остановимся. Докажем, что мы в какой-то момент остановимся. Пусть в призвольный момент времени &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид и размер её наддерева меньше &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, значит максимальное поддерево имеет размер больше чем &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, т.е. &amp;lt;math&amp;gt;|subtree(u)| &amp;gt; \frac{n}{2}&amp;lt;/math&amp;gt;, а значит размер &amp;quot;наддерева&amp;quot; вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; равен &amp;lt;tex&amp;gt;n - |subtree(u)| &amp;lt; \frac{n}{2}&amp;lt;/tex&amp;gt;. При этом теперь размер любого поддерева, на которое распадется дерево t при удалении вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; не превосходит &amp;lt;math&amp;gt;|subtree(u)| - 1&amp;lt;/math&amp;gt;, т.к. наддерево имеет размер меньше, чем поддерево &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, а любое поддерево вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; имеет хотя бы на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; вершину меньше (сама вершина &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;). По индукции получаем, что в любой момент времени размер наддерева вершины v меньше &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, значит мы будем спускаться только вниз по дереву &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, и при переходе к вершине &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} сыну &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; размер максимального поддерева уменьшится как минимум на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Значит не более чем за &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; шагов наши действия прекратятся и мы окажемся в центроиде дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
Итак, мы конструктивно доказали существование центроида и привели линейный относительно размера дерева алгоритм его нахождения. &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Реализация ===&lt;br /&gt;
&lt;br /&gt;
Поиск центроида в дереве:&lt;br /&gt;
&lt;br /&gt;
  '''int''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int''' sz[n])&lt;br /&gt;
     max_subtree = -1&lt;br /&gt;
     '''for''' u : children(v)&lt;br /&gt;
          '''if''' sz[u] &amp;gt; sz[v] / 2 and sz[u] &amp;gt; sz[max_subtree] &amp;lt;font color=green&amp;gt;// считаем sz[-1] = 0 &amp;lt;/font &amp;gt;&lt;br /&gt;
               max_subtree = u&lt;br /&gt;
     '''if''' max_subtree == -1&lt;br /&gt;
          return v&lt;br /&gt;
     '''else''' &lt;br /&gt;
          '''return''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz)&lt;br /&gt;
&lt;br /&gt;
Шаблон решения произвольной задачи на статическую центроидную декомпозицию:&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int''' sz[n])&lt;br /&gt;
     c = &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz) &amp;lt;font color=green&amp;gt;// находим c {{---}} центроид поддерева вершины v &amp;lt;/font &amp;gt;&lt;br /&gt;
     ch = children[c]&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{deleteEdges}&amp;lt;/tex&amp;gt;(c, children) &amp;lt;font color=green&amp;gt;// удаляем все ребра между вершиной с и детьми, чтобы мы не смогли из детей попасть в с. &lt;br /&gt;
         Это полезно делать, если решение подзадачи для поддерева предполагает проход dfs &amp;lt;/font &amp;gt;&lt;br /&gt;
     '''for''' c2 : ch[c]&lt;br /&gt;
          &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;(children, c2, sz)&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{mergeSolution}&amp;lt;/tex&amp;gt;(children, c2) &amp;lt;font color=green&amp;gt;// решаем для текущего поддерева &amp;lt;/font &amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Динамическая центроидная декомпозиция (дерево центроидной декомпозиции) ==&lt;br /&gt;
Теперь вернемся ко второй задаче введения. Для ее решения мы пользовались динамичекой версией divide &amp;amp; conquer {{---}} деревом отрезков. В предыдущем пункте мы определили статический аналог divide &amp;amp; conquer для дерева. Теперь обобщим этот метод для динамических задач.&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Деревом центроидов (или центроидной декомпозицией)''' дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; называют дерево &amp;lt;math&amp;gt;T(t)&amp;lt;/math&amp;gt;, построенное на вершинах дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; следующим образом :&lt;br /&gt;
* Пусть вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} центроид дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Объявим его корнем нового дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пусть при удалении вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; из дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; оно распалось на поддеревья &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, тогда детьми вершины c объявим &amp;lt;tex&amp;gt;T(t_1), T(t_t),\dots, T(t_k)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
=== Свойства центроидной декомпозиции ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
'''Свойства центроидной декомпозиции''' :&lt;br /&gt;
# Глубина дерева центроидов не превосходит &amp;lt;tex&amp;gt;log(n)&amp;lt;/tex&amp;gt;. &lt;br /&gt;
# Каждая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; является центроидом одного из поддеревьев дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждая вершина дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; поддеревьям дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (еще говорят, что вершина принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; центроидам дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, или что эти центроиды содержат вершину)&lt;br /&gt;
# Для любых вершин &amp;lt;tex&amp;gt;u, v \in T (u \neq v)&amp;lt;/tex&amp;gt; верно ровно одно из следующих трех утверждений: &lt;br /&gt;
a) &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;&lt;br /&gt;
b) &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;&lt;br /&gt;
c) &amp;lt;tex&amp;gt;T(u) \cap T(v) = \emptyset &amp;lt;/tex&amp;gt;&lt;br /&gt;
# Простой путь между любой парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; содержит центроид &amp;lt;tex&amp;gt;c \in T(t)&amp;lt;/tex&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
# Действительно, т.к. размер поддерева &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; не превосходит &amp;lt;tex&amp;gt;\frac{|subtree(c)|}{2}&amp;lt;/tex&amp;gt;, то при спуске в каждую следующую вершину на пути к любому листу в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; размер поддерева вершины, в которой мы сейчас находимся, уменьшается как минимум на &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;. Значит длина всего пути до листа не превосходит &amp;lt;math&amp;gt;log(n)&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
# Второе свойство очевидно из построения дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, т.к. если вершина принадлежит дереву центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то она является центроидом, а из построения &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы знаем, что каждая вершина исходного дерева принадлежит и дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Третье свойство {{---}} прямое следствие первых двух, т.к. вершина принадлежит любому центроиду &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; т.и т.т., когда c {{---}} отец вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов. Т.к. вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; точно принадлежит дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (свойство 2), то она лежит на каком-то пути в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, причем все ее родители (центроиды) ее содержат. А по свойству 1 длина любого вертикального (и даже простого) пути есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
# Четвертое свойство очевидно из того, что &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; {{---}} дерево. Т.к. &amp;lt;math&amp;gt;T(u)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;T(v)&amp;lt;/math&amp;gt; {{---}} поддеревья различных вершин дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то либо они не пересекаются, либо &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;, либо &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Для доказательства последнего свойства выберем в качестве вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;lca(u, v)&amp;lt;/math&amp;gt; в дереве центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Покажем, что так выбранная вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удовлетворяет заявленным свойствам. То, что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt; {{---}} очевидно по определению &amp;lt;math&amp;gt;lca&amp;lt;/math&amp;gt;, т.к. каждый предок любой вершины в дереве центроидов содержит эту вершину. Теперь докажем, что &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежит на пути между парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt;. Т.к. &amp;lt;math&amp;gt;c = lca(u, v)&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то из &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; нет ребра в такого сына, который содержит одновременно &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в своем поддереве (в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;), значит после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; разделится на несколько поддеревьев, таких что вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; окажутся в разных компонентах связности. А значит найдется такое ребро &amp;lt;math&amp;gt;(c, x)&amp;lt;/math&amp;gt;, которое принадлежало пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, но после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удалилось. Это доказывает то, что вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежала на пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
С помощью описанных свойств дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы можем решить задачу 2 для дерева :&lt;br /&gt;
&lt;br /&gt;
=== Пример решения задачи с помощью центроидной декомпозиции === &lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин. В каждый момент времени любая вершина дерева может быть либо помечена, либо нет. Изначально помечена только вершина номер &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Дан список из &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; запросов: &lt;br /&gt;
* Вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; пометили.&lt;br /&gt;
* С вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; сняли пометку.&lt;br /&gt;
* Найти номер ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
'''Решение :'''&lt;br /&gt;
&lt;br /&gt;
Построим центроидную декомпозицию &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Изначально посчитаем для каждого центроида, содержащего вершину &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt; посчитаем расстояние до вершины &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Для этого воспользуемся [[Метод_двоичного_подъема|методом двоичных подъемов для поиска lca пары вершин в дереве]], а также тем фактом, что если глубина &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве определена как расстояние от корня до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то длина пути между парой вершин &amp;lt;math&amp;gt;(u, v)&amp;lt;/math&amp;gt; есть &amp;lt;tex&amp;gt;len(u, v) = h(u) + h(v) - 2 \cdot h(lca(u, v))&amp;lt;/tex&amp;gt;. Если изначально предпосчитать проходом [[Обход_в_глубину,_цвета_вершин| dfs]] величины &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, то ответ на запрос &amp;lt;math&amp;gt;len(u, v)&amp;lt;/math&amp;gt; можно делать за время &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с &amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt;доп. памятью. Также можно воспользоваться техникой [[Сведение_задачи_LCA_к_задаче_RMQ|сведения задачи LCA к RMQ]] и решить с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; времени на запрос. Теперь научимся отвечать на запросы. Из последнего свойства центроидной декомпозиции видно, что если &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} искомая ближайшая помеченная вершина к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то путь между ними содержит центроид &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;, причем &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок одновременно вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве&amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Поэтому заведем [[Красно-черное_дерево|двоичное дерево поиска]] для каждого центроида &amp;lt;math&amp;gt;c \in T&amp;lt;/math&amp;gt;. В этой структуре для каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; будем хранить пары &amp;lt;math&amp;gt;(len(c, u), u)&amp;lt;/math&amp;gt; для всех помеченных вершин &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в поддереве центроидов &amp;lt;math&amp;gt;T(c)&amp;lt;/math&amp;gt;. Когда приходит запрос пометить вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} добавим в структуру данных для всех предков &amp;lt;math&amp;gt;p_c&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; пары &amp;lt;math&amp;gt;(len(p_c, v), v)&amp;lt;/math&amp;gt;. Мы совершим &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; добавлений, затратив &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; действий на каждое. Запрос снятия пометки с вершины обрабатывается аналогичными удалениями. Запрос поиска ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины {{---}} это запрос поиска вершины u, такой что  величина &amp;lt;math&amp;gt;len(c, u) + len(c, v)&amp;lt;/math&amp;gt; минимальна, где &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов (по пятому свойству, нас интересуют именно такие &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;). Этот запрос занимает так же &amp;lt;tex&amp;gt;O(log^2(n))&amp;lt;/tex&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
Итак, мы научились решать задачу с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и временной сложностью &amp;lt;math&amp;gt;O(log^2(n))&amp;lt;/math&amp;gt; на запрос любого типа с предварительным предпосчетом за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Варианты хранения центроидной декомпозиции ==&lt;br /&gt;
Для хранения дерева центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; есть &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; подхода, имеющих свои преимущества и недостатки :&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева запомним величину &amp;lt;math&amp;gt;p_v&amp;lt;/math&amp;gt; {{---}} номер предка вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Этот подход наиболее экономный по памяти (&amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;), но уступает в скорости и функциональности.&lt;br /&gt;
&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева будем хранить весь массив предков &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt; в дереве центроидов.&lt;br /&gt;
&lt;br /&gt;
Этот подход уступает в количестве необходимой дополнительной памяти (&amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt; суммарно), но имеет ряд преимуществ :&lt;br /&gt;
&lt;br /&gt;
#При проходе по массиву предков фиксированной вершины будет выигрыш в скорости работы, т.к. весь массив будет лежать непрерывным блоком данных и следовательно будет закэширован&lt;br /&gt;
# На массиве предков можно строить различные структуры данных (такие как, например, дерево отрезков) для быстрого (в случае с деревом отрезков &amp;lt;math&amp;gt;O(log(log(n)))&amp;lt;/math&amp;gt; на запрос) поиска предка с необходимыми свойствами. Так, например, в описанной выше задаче про помеченные вершины наибольшего общего предка можно искать методом двоичных подъемов за &amp;lt;math&amp;gt;O(log(log(n))&amp;lt;/math&amp;gt; на запрос, т.к. размер массива предков есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; (по свойству 1 центроидной декомпозиции). Используя эту оптимизацию можно получить время &amp;lt;math&amp;gt;O(log(n)) + O(log(log(n)) = O(log(n))&amp;lt;/math&amp;gt; на запрос нахождения ближайшей помеченной вершины. Чтобы добиться улучшенной асимптотики для запросов изменения можно хранить дерево отрезков на каждом из путей &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt;, в каждой вершине которого хранить двоичное дерево поиска и поддерживать отложенные операции. Тогда ответ на эти запросы будет занимать &amp;lt;math&amp;gt;O(log(n) \cdot log(log(n))&amp;lt;/math&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
&lt;br /&gt;
*[[:Дерево_отрезков._Построение|Дерево отрезков]]&lt;br /&gt;
*[[:Heavy-light_декомпозиция|Heavy-light декомпозиция]]&lt;br /&gt;
*[[:Метод_двоичного_подъема| Метод двоичного подъема]]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы и структуры данных]]&lt;/div&gt;</summary>
		<author><name>92.255.113.52</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61634</id>
		<title>Centroid decomposition</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61634"/>
				<updated>2017-06-14T21:42:19Z</updated>
		
		<summary type="html">&lt;p&gt;92.255.113.52: /* Свойства центроидной декомпозиции */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Centroid decomposition (рус. центроидная декомпозиция) {{---}} это структура данных, позволяющая отвечать на запросы на дереве. Чаще всего это запросы, связанные с нахождением функции на вершинах, связанных неравенством на расстояние между ними в дереве. Также иногда применяется для запросов на путях в дереве.&lt;br /&gt;
&lt;br /&gt;
== Введение ==&lt;br /&gt;
Рассмотрим &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; задачи на обычном массиве (в дальнейшем мы будем их обобщать на случай дерева):&lt;br /&gt;
&lt;br /&gt;
Задача &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть массив &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; положительных целых чисел из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов. Также дано число &amp;lt;tex&amp;gt;W \geqslant 0&amp;lt;/tex&amp;gt; и число &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; индексов массива, таких что &amp;lt;tex&amp;gt;|j - i| \leqslant l &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\sum_{i=0}^{n - 1} a_i \leqslant W&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
Задача &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть прямая дорога, на которой расположены &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; городов. В некоторых городах есть госпитали, которые могут принимать больных. Поступают запросы вида :&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;, в котором находится больной и требуется найти такой город &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;|u - v|&amp;lt;/tex&amp;gt; минимально возможное.&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что больше он не будет принимать больных&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что теперь он может принимать больных&lt;br /&gt;
}}&lt;br /&gt;
Для начала решим обе задачи. &lt;br /&gt;
Первая задача решается методом [[Сортировка_слиянием|divide &amp;amp; conquer (рус. разделяй и властвуй)]] {{---}} давайте разделим массив &amp;lt;tex&amp;gt;a[0 \dots n-1]&amp;lt;/tex&amp;gt; на 2 массива &amp;lt;tex&amp;gt;a[0\dots\frac{n}{2} - 1]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;a[\frac{n}{2} \dots n-1]&amp;lt;/tex&amp;gt; и рекурсивно решим задачу для каждого из них. Осталось научиться находить количество искомых пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt;, таких что &amp;lt;tex&amp;gt;i &amp;lt; \frac{n}{2}, j \geqslant \frac{n}{2}&amp;lt;/tex&amp;gt;. Для этого воспользуемся другой известной техникой {{---}} методом двух указателей. Посчитаем массив префиксных сумм для правой половины &amp;lt;tex&amp;gt;pref[i] = \sum_{j=\frac{n}{2}}^{i} a_j&amp;lt;/tex&amp;gt; и суффиксных (&amp;lt;tex&amp;gt;suf[i] = \sum_{j=i}^{\frac{n}{2} + 1} a_j&amp;lt;/tex&amp;gt;) {{---}} для левой. Заведем два указателя (&amp;lt;tex&amp;gt;p_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;p_2&amp;lt;/tex&amp;gt;). Изначально установим &amp;lt;tex&amp;gt;p_1 = \frac{n}{2} - l + 1, p_2 = \frac{n}{2}&amp;lt;/tex&amp;gt;. Пока &amp;lt;tex&amp;gt;p_2 - 1&amp;gt; \frac{n}{2}&amp;lt;/tex&amp;gt; и&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;pref[p_2] + suf[p_1] &amp;gt; W &amp;lt;/tex&amp;gt; будем уменьшать &amp;lt;math&amp;gt;p_2&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Если после этого &amp;lt;math&amp;gt;pref[p_2] + suf[p_1] \leqslant W&amp;lt;/math&amp;gt;, то к ответу прибавим &amp;lt;math&amp;gt;(p_2 - \frac{n}{2} + 1) * (\frac{n}{2} - p_1)&amp;lt;/math&amp;gt;, посго, увеличим &amp;lt;math&amp;gt;p_1&amp;lt;/math&amp;gt; на &amp;lt;math/math&amp;gt;. Так будем делать, пока &amp;lt;math&amp;gt;p_1 &amp;lt; \frac{n}{2}&amp;lt;/math&amp;gt;. В конце сложим текущий ответ и ответы для половин массива {{---}} получим ответ на задачу. Асимптотика такого алгоритма: &amp;lt;tex&amp;gt;T(n) = 2 * T(n / 2) + O(n) = O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вторая задача имеет запросы на изменение и поэтому надо применить динамическую версию divide &amp;amp; conquer {{---}} [[Дерево_отрезков._Построение|дерево отрезков]]. Построим дерево отрезков,&lt;br /&gt;
поддерживающее 2 вида запросов: присвоение в точке и минимум на отрезке. Изначально сделаем так, чтобы дереву отрезков соответствовал массив &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;b_i = 0&amp;lt;/tex&amp;gt;, если в i-м городе принимает госпиталь и &amp;lt;tex&amp;gt;b_i = 1&amp;lt;/tex&amp;gt; иначе. Когда в каком-то городе открывается/закрывается госпиталь {{---}} делаем запрос на изменение в дереве отрезков. Когда требуется узнать ближайщий госпиталь к &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-му городу, можно воспользоваться одной из следующих идей: &lt;br /&gt;
а) (&amp;lt;tex&amp;gt;O(n \cdot log^2(n)&amp;lt;/tex&amp;gt;) Бинарным поиском ищем ближайший слева и ближайший справа к i-му городу госпиталь (такой город &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, что &amp;lt;tex&amp;gt;\min\limits_{k=i..j}b_k= 1&amp;lt;/tex&amp;gt;). Для этого внутри бинарного поиска каждый раз делаем запрос на поиск минимума в дереве отрезков.  &lt;br /&gt;
б) (&amp;lt;tex&amp;gt;O(n \cdot log(n)&amp;lt;/tex&amp;gt;) Будем одним спуском/подъемом по дереву определять, куда нам нужно идти (в левое или правое поддерево), тем самым делая одновоременно и бинарный поиск, и спуск/подъем по дереву.&lt;br /&gt;
&lt;br /&gt;
== Статическая центроидная декомпозиция ==&lt;br /&gt;
Перейдем к обобщению поставленных задач на случай дерева. Начнем, как и полагается, с первой:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть взвешенное дерево &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин, в каждой вершине которого написаны положительные целые числа. Также по-прежнему даны числа &amp;lt;tex&amp;gt;W &amp;gt;= 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; вершин дерева, таких что расстояние между ними не превосходит &amp;lt;math&amp;gt;l&amp;lt;/math&amp;gt; по числу ребер и не превосходит &amp;lt;math&amp;gt;W&amp;lt;/math&amp;gt; по сумме весов.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Для решения новой задачи применим ту же идею, что и была до этого {{---}} разделяй и властвуй. Для этого нам потребуется следующий объект :&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|id=191213&lt;br /&gt;
|definition =&lt;br /&gt;
'''Центроидом дерева''' (англ. ''centroid'') называется такая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, после удаления которой дерево разбивается на несколько (&amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;) поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, таких что для каждого &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;: &amp;lt;tex&amp;gt;|t_i| \leqslant \frac{n}{2}&amp;lt;/tex&amp;gt;, т.е. размер каждого поддерева не превосходит половины размера исходного дерева.&lt;br /&gt;
}}&lt;br /&gt;
Итак, в случае дерева идея разделяй-и-властвуй из предыдущего пункта будет формулироваться так: найдем центроид. Предположим, что мы сумели найти центроид за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; {{---}} размер дерева. Тогда, как и в упрощенной версии задачи {{---}} рекурсивно найдем ответ для всех поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, после чего попытаемся найти недостающие пары вершин, находящиехя в разных поддеревьях и удовлетворяющих вопросу задачи. Для этого будем отвечать на следующие запросы: пусть мы сейчас считаем все пары, где первая из вершин находится в поддереве &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и мы в некоторой структуре данных &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; храним все вершины остальных деревьев (каждую вершину задаем парой &amp;lt;math&amp;gt;(depth(v), length(v))&amp;lt;/math&amp;gt; {{---}} глубина вершины и длина пути до нее из корня поддерева), расстояние до которых от корня их поддерева не превышает &amp;lt;math&amp;gt;min(l, n)&amp;lt;/math&amp;gt;. Тогда просто пройдемся по всем вершинам &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; поддерева &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и прибавим к ответу число вершин в структуре &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt;, таких, что &amp;lt;tex&amp;gt;depth(u) \leqslant l - depth(v)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;length(u) \leqslant l - length(v)&amp;lt;/tex&amp;gt;. Это двумерные запросы, на которые можно отвечать за &amp;lt;math&amp;gt;O(log^2(n)&amp;lt;/math&amp;gt; с помощью [[Многомерное_дерево_отрезков|2d-дерева отрезков]], либо за &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с помощью [[Перечисление_точек_в_произвольном_прямоугольнике_за_n_*_log_%5E(d_-_1)_n_(range_tree)|техники поиска точек в d-мерном пространстве]]. Также читателю предлагается придумать и более эффективные и простые способы решить эту подзадачу.&lt;br /&gt;
&lt;br /&gt;
Оценим итоговую асимптотику: &amp;lt;tex&amp;gt;T(n) = k * T(n / k) + O(n \cdot log(n))&amp;lt;/tex&amp;gt;. Решая это рекурентное соотношение, получим &amp;lt;math&amp;gt;O(n \cdot log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь, как и было обещено, докажем лемму о существовании центроида и опишем алгоритм его эффективного поиска.&lt;br /&gt;
&lt;br /&gt;
=== Лемма о существовании центроида и алгоритм его нахождения. ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
В любом дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; существует центроид.&lt;br /&gt;
|proof=&lt;br /&gt;
Рассмотрим корень дерева &amp;lt;math&amp;gt;(r)&amp;lt;/math&amp;gt;. Положим изначально &amp;lt;math&amp;gt;v = r&amp;lt;/math&amp;gt;. Изначально &amp;lt;math&amp;gt;|subtree(v)| = n&amp;lt;/math&amp;gt;. Среди всех детей &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; выберем вершину &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; с максимальным размером поддерева. Если &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид, то положим &amp;lt;math&amp;gt;v = u&amp;lt;/math&amp;gt; и продолжим выбор нового u, иначе {{---}} остановимся. Докажем, что мы в какой-то момент остановимся. Пусть в призвольный момент времени &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид и размер её наддерева меньше &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, значит максимальное поддерево имеет размер больше чем &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, т.е. &amp;lt;math&amp;gt;|subtree(u)| &amp;gt; \frac{n}{2}&amp;lt;/math&amp;gt;, а значит размер &amp;quot;наддерева&amp;quot; вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; равен &amp;lt;tex&amp;gt;n - |subtree(u)| &amp;lt; \frac{n}{2}&amp;lt;/tex&amp;gt;. При этом теперь размер любого поддерева, на которое распадется дерево t при удалении вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; не превосходит &amp;lt;math&amp;gt;|subtree(u)| - 1&amp;lt;/math&amp;gt;, т.к. наддерево имеет размер меньше, чем поддерево &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, а любое поддерево вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; имеет хотя бы на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; вершину меньше (сама вершина &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;). По индукции получаем, что в любой момент времени размер наддерева вершины v меньше &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, значит мы будем спускаться только вниз по дереву &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, и при переходе к вершине &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} сыну &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; размер максимального поддерева уменьшится как минимум на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Значит не более чем за &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; шагов наши действия прекратятся и мы окажемся в центроиде дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
Итак, мы конструктивно доказали существование центроида и привели линейный относительно размера дерева алгоритм его нахождения. &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Реализация ===&lt;br /&gt;
&lt;br /&gt;
Поиск центроида в дереве:&lt;br /&gt;
&lt;br /&gt;
  '''int''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int''' sz[n])&lt;br /&gt;
     max_subtree = -1&lt;br /&gt;
     '''for''' u : children(v)&lt;br /&gt;
          '''if''' sz[u] &amp;gt; sz[v] / 2 and sz[u] &amp;gt; sz[max_subtree] &amp;lt;font color=green&amp;gt;// считаем sz[-1] = 0 &amp;lt;/font &amp;gt;&lt;br /&gt;
               max_subtree = u&lt;br /&gt;
     '''if''' max_subtree == -1&lt;br /&gt;
          return v&lt;br /&gt;
     '''else''' &lt;br /&gt;
          '''return''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz)&lt;br /&gt;
&lt;br /&gt;
Шаблон решения произвольной задачи на статическую центроидную декомпозицию:&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int''' sz[n])&lt;br /&gt;
     c = &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz) &amp;lt;font color=green&amp;gt;// находим c {{---}} центроид поддерева вершины v &amp;lt;/font &amp;gt;&lt;br /&gt;
     ch = children[c]&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{deleteEdges}&amp;lt;/tex&amp;gt;(c, children) &amp;lt;font color=green&amp;gt;// удаляем все ребра между вершиной с и детьми, чтобы мы не смогли из детей попасть в с. &lt;br /&gt;
         Это полезно делать, если решение подзадачи для поддерева предполагает проход dfs &amp;lt;/font &amp;gt;&lt;br /&gt;
     '''for''' c2 : ch[c]&lt;br /&gt;
          &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;(children, c2, sz)&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{mergeSolution}&amp;lt;/tex&amp;gt;(children, c2) &amp;lt;font color=green&amp;gt;// решаем для текущего поддерева &amp;lt;/font &amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Динамическая центроидная декомпозиция (дерево центроидной декомпозиции) ==&lt;br /&gt;
Теперь вернемся ко второй задаче введения. Для ее решения мы пользовались динамичекой версией divide &amp;amp; conquer {{---}} деревом отрезков. В предыдущем пункте мы определили статический аналог divide &amp;amp; conquer для дерева. Теперь обобщим этот метод для динамических задач.&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Деревом центроидов (или центроидной декомпозицией)''' дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; называют дерево &amp;lt;math&amp;gt;T(t)&amp;lt;/math&amp;gt;, построенное на вершинах дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; следующим образом :&lt;br /&gt;
* Пусть вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} центроид дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Объявим его корнем нового дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пусть при удалении вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; из дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; оно распалось на поддеревья &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, тогда детьми вершины c объявим &amp;lt;tex&amp;gt;T(t_1), T(t_t),\dots, T(t_k)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
=== Свойства центроидной декомпозиции ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
'''Свойства центроидной декомпозиции''' :&lt;br /&gt;
# Глубина дерева центроидов не превосходит &amp;lt;tex&amp;gt;log(n)&amp;lt;/tex&amp;gt;. &lt;br /&gt;
# Каждая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; является центроидом одного из поддеревьев дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждая вершина дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; поддеревьям дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (еще говорят, что вершина принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; центроидам дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, или что эти центроиды содержат вершину)&lt;br /&gt;
# Для любых вершин &amp;lt;tex&amp;gt;u, v \in T (u \neq v)&amp;lt;/tex&amp;gt; верно ровно одно из следующих трех утверждений: &lt;br /&gt;
a) &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;&lt;br /&gt;
b) &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;&lt;br /&gt;
c) &amp;lt;tex&amp;gt;T(u) \cap T(v) = \emptyset &amp;lt;/tex&amp;gt;&lt;br /&gt;
# Простой путь между любой парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; содержит центроид &amp;lt;tex&amp;gt;c \in T(t)&amp;lt;/tex&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
# Действительно, т.к. размер поддерева &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; не превосходит &amp;lt;tex&amp;gt;\frac{|subtree(c)|}{2}&amp;lt;/tex&amp;gt;, то при спуске в каждую следующую вершину на пути к любому листу в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; размер поддерева вершины, в которой мы сейчас находимся, уменьшается как минимум на &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;. Значит длина всего пути до листа не превосходит &amp;lt;math&amp;gt;log(n)&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
# Второе свойство очевидно из построения дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, т.к. если вершина принадлежит дереву центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то она является центроидом, а из построения &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы знаем, что каждая вершина исходного дерева принадлежит и дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Третье свойство {{---}} прямое следствие первых двух, т.к. вершина принадлежит любому центроиду &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; т.и т.т., когда c {{---}} отец вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов. Т.к. вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; точно принадлежит дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (свойство 2), то она лежит на каком-то пути в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, причем все ее родители (центроиды) ее содержат. А по свойству 1 длина любого вертикального (и даже простого) пути есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
# Четвертое свойство очевидно из того, что &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; {{---}} дерево. Т.к. &amp;lt;math&amp;gt;T(u)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;T(v)&amp;lt;/math&amp;gt; {{---}} поддеревья различных вершин дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то либо они не пересекаются, либо &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;, либо &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Для доказательства последнего свойства выберем в качестве вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;lca(u, v)&amp;lt;/math&amp;gt; в дереве центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Покажем, что так выбранная вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удовлетворяет заявленным свойствам. То, что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt; {{---}} очевидно по определению &amp;lt;math&amp;gt;lca&amp;lt;/math&amp;gt;, т.к. каждый предок любой вершины в дереве центроидов содержит эту вершину. Теперь докажем, что &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежит на пути между парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt;. Т.к. &amp;lt;math&amp;gt;c = lca(u, v)&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то из &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; нет ребра в такого сына, который содержит одновременно &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в своем поддереве (в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;), значит после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; разделится на несколько поддеревьев, таких что вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; окажутся в разных компонентах связности. А значит найдется такое ребро &amp;lt;math&amp;gt;(c, x)&amp;lt;/math&amp;gt;, которое принадлежало пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, но после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удалилось. Это доказывает то, что вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежала на пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
С помощью описанных свойств дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы можем решить задачу 2 для дерева :&lt;br /&gt;
&lt;br /&gt;
=== Пример решения задачи с помощью центроидной декомпозиции === &lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин. В каждый момент времени любая вершина дерева может быть либо помечена, либо нет. Изначально помечена только вершина номер &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Дан список из &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; запросов: &lt;br /&gt;
* Вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; пометили.&lt;br /&gt;
* С вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; сняли пометку.&lt;br /&gt;
* Найти номер ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
'''Решение :'''&lt;br /&gt;
&lt;br /&gt;
Построим центроидную декомпозицию &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Изначально посчитаем для каждого центроида, содержащего вершину &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt; посчитаем расстояние до вершины &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Для этого воспользуемся [[Метод_двоичного_подъема|методом двоичных подъемов для поиска lca пары вершин в дереве]], а также тем фактом, что если глубина &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве определена как расстояние от корня до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то длина пути между парой вершин &amp;lt;math&amp;gt;(u, v)&amp;lt;/math&amp;gt; есть &amp;lt;tex&amp;gt;len(u, v) = h(u) + h(v) - 2 \cdot h(lca(u, v))&amp;lt;/tex&amp;gt;. Если изначально предпосчитать проходом [[Обход_в_глубину,_цвета_вершин| dfs]] величины &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, то ответ на запрос &amp;lt;math&amp;gt;len(u, v)&amp;lt;/math&amp;gt; можно делать за время &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с &amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt;доп. памятью. Также можно воспользоваться техникой [[Сведение_задачи_LCA_к_задаче_RMQ|сведения задачи LCA к RMQ]] и решить с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; времени на запрос. Теперь научимся отвечать на запросы. Из последнего свойства центроидной декомпозиции видно, что если &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} искомая ближайшая помеченная вершина к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то путь между ними содержит центроид &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;, причем &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок одновременно вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве&amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Поэтому заведем [[Красно-черное_дерево|двоичное дерево поиска]] для каждого центроида &amp;lt;math&amp;gt;c \in T&amp;lt;/math&amp;gt;. В этой структуре для каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; будем хранить пары &amp;lt;math&amp;gt;(len(c, u), u)&amp;lt;/math&amp;gt; для всех помеченных вершин &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в поддереве центроидов &amp;lt;math&amp;gt;T(c)&amp;lt;/math&amp;gt;. Когда приходит запрос пометить вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} добавим в структуру данных для всех предков &amp;lt;math&amp;gt;p_c&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; пары &amp;lt;math&amp;gt;(len(p_c, v), v)&amp;lt;/math&amp;gt;. Мы совершим &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; добавлений, затратив &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; действий на каждое. Запрос снятия пометки с вершины обрабатывается аналогичными удалениями. Запрос поиска ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины {{---}} это запрос поиска вершины u, такой что  величина &amp;lt;math&amp;gt;len(c, u) + len(c, v)&amp;lt;/math&amp;gt; минимальна, где &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов (по пятому свойству, нас интересуют именно такие &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;). Этот запрос занимает так же &amp;lt;tex&amp;gt;O(log^2(n))&amp;lt;/tex&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
Итак, мы научились решать задачу с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и временной сложностью &amp;lt;math&amp;gt;O(log^2(n))&amp;lt;/math&amp;gt; на запрос любого типа с предварительным предпосчетом за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Варианты хранения центроидной декомпозиции ==&lt;br /&gt;
Для хранения дерева центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; есть &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; подхода, имеющих свои преимущества и недостатки :&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева запомним величину &amp;lt;math&amp;gt;p_v&amp;lt;/math&amp;gt; {{---}} номер предка вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Этот подход наиболее экономный по памяти (&amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;), но уступает в скорости и функциональности.&lt;br /&gt;
&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева будем хранить весь массив предков &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt; в дереве центроидов.&lt;br /&gt;
&lt;br /&gt;
Этот подход уступает в количестве необходимой дополнительной памяти (&amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt; суммарно), но имеет ряд преимуществ :&lt;br /&gt;
&lt;br /&gt;
#При проходе по массиву предков фиксированной вершины будет выигрыш в скорости работы, т.к. весь массив будет лежать непрерывным блоком данных и следовательно будет закэширован&lt;br /&gt;
# На массиве предков можно строить различные структуры данных (такие как, например, дерево отрезков) для быстрого (в случае с деревом отрезков &amp;lt;math&amp;gt;O(log(log(n)))&amp;lt;/math&amp;gt; на запрос) поиска предка с необходимыми свойствами. Так, например, в описанной выше задаче про помеченные вершины наибольшего общего предка можно искать методом двоичных подъемов за &amp;lt;math&amp;gt;O(log(log(n))&amp;lt;/math&amp;gt; на запрос, т.к. размер массива предков есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; (по свойству 1 центроидной декомпозиции). Используя эту оптимизацию можно получить время &amp;lt;math&amp;gt;O(log(n)) + O(log(log(n)) = O(log(n))&amp;lt;/math&amp;gt; на запрос нахождения ближайшей помеченной вершины. Чтобы добиться улучшенной асимптотики для запросов изменения можно хранить дерево отрезков на каждом из путей &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt;, в каждой вершине которого хранить двоичное дерево поиска и поддерживать отложенные операции. Тогда ответ на эти запросы будет занимать &amp;lt;math&amp;gt;O(log(n) \cdot log(log(n))&amp;lt;/math&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
&lt;br /&gt;
*[[:Дерево_отрезков._Построение|Дерево отрезков]]&lt;br /&gt;
*[[:Heavy-light_декомпозиция|Heavy-light декомпозиция]]&lt;br /&gt;
*[[:Метод_двоичного_подъема| Метод двоичного подъема]]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы и структуры данных]]&lt;/div&gt;</summary>
		<author><name>92.255.113.52</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61633</id>
		<title>Centroid decomposition</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61633"/>
				<updated>2017-06-14T21:42:01Z</updated>
		
		<summary type="html">&lt;p&gt;92.255.113.52: /* Свойства центроидной декомпозиции */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Centroid decomposition (рус. центроидная декомпозиция) {{---}} это структура данных, позволяющая отвечать на запросы на дереве. Чаще всего это запросы, связанные с нахождением функции на вершинах, связанных неравенством на расстояние между ними в дереве. Также иногда применяется для запросов на путях в дереве.&lt;br /&gt;
&lt;br /&gt;
== Введение ==&lt;br /&gt;
Рассмотрим &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; задачи на обычном массиве (в дальнейшем мы будем их обобщать на случай дерева):&lt;br /&gt;
&lt;br /&gt;
Задача &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть массив &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; положительных целых чисел из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов. Также дано число &amp;lt;tex&amp;gt;W \geqslant 0&amp;lt;/tex&amp;gt; и число &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; индексов массива, таких что &amp;lt;tex&amp;gt;|j - i| \leqslant l &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\sum_{i=0}^{n - 1} a_i \leqslant W&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
Задача &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть прямая дорога, на которой расположены &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; городов. В некоторых городах есть госпитали, которые могут принимать больных. Поступают запросы вида :&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;, в котором находится больной и требуется найти такой город &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;|u - v|&amp;lt;/tex&amp;gt; минимально возможное.&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что больше он не будет принимать больных&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что теперь он может принимать больных&lt;br /&gt;
}}&lt;br /&gt;
Для начала решим обе задачи. &lt;br /&gt;
Первая задача решается методом [[Сортировка_слиянием|divide &amp;amp; conquer (рус. разделяй и властвуй)]] {{---}} давайте разделим массив &amp;lt;tex&amp;gt;a[0 \dots n-1]&amp;lt;/tex&amp;gt; на 2 массива &amp;lt;tex&amp;gt;a[0\dots\frac{n}{2} - 1]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;a[\frac{n}{2} \dots n-1]&amp;lt;/tex&amp;gt; и рекурсивно решим задачу для каждого из них. Осталось научиться находить количество искомых пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt;, таких что &amp;lt;tex&amp;gt;i &amp;lt; \frac{n}{2}, j \geqslant \frac{n}{2}&amp;lt;/tex&amp;gt;. Для этого воспользуемся другой известной техникой {{---}} методом двух указателей. Посчитаем массив префиксных сумм для правой половины &amp;lt;tex&amp;gt;pref[i] = \sum_{j=\frac{n}{2}}^{i} a_j&amp;lt;/tex&amp;gt; и суффиксных (&amp;lt;tex&amp;gt;suf[i] = \sum_{j=i}^{\frac{n}{2} + 1} a_j&amp;lt;/tex&amp;gt;) {{---}} для левой. Заведем два указателя (&amp;lt;tex&amp;gt;p_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;p_2&amp;lt;/tex&amp;gt;). Изначально установим &amp;lt;tex&amp;gt;p_1 = \frac{n}{2} - l + 1, p_2 = \frac{n}{2}&amp;lt;/tex&amp;gt;. Пока &amp;lt;tex&amp;gt;p_2 - 1&amp;gt; \frac{n}{2}&amp;lt;/tex&amp;gt; и&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;pref[p_2] + suf[p_1] &amp;gt; W &amp;lt;/tex&amp;gt; будем уменьшать &amp;lt;math&amp;gt;p_2&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Если после этого &amp;lt;math&amp;gt;pref[p_2] + suf[p_1] \leqslant W&amp;lt;/math&amp;gt;, то к ответу прибавим &amp;lt;math&amp;gt;(p_2 - \frac{n}{2} + 1) * (\frac{n}{2} - p_1)&amp;lt;/math&amp;gt;, посго, увеличим &amp;lt;math&amp;gt;p_1&amp;lt;/math&amp;gt; на &amp;lt;math/math&amp;gt;. Так будем делать, пока &amp;lt;math&amp;gt;p_1 &amp;lt; \frac{n}{2}&amp;lt;/math&amp;gt;. В конце сложим текущий ответ и ответы для половин массива {{---}} получим ответ на задачу. Асимптотика такого алгоритма: &amp;lt;tex&amp;gt;T(n) = 2 * T(n / 2) + O(n) = O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вторая задача имеет запросы на изменение и поэтому надо применить динамическую версию divide &amp;amp; conquer {{---}} [[Дерево_отрезков._Построение|дерево отрезков]]. Построим дерево отрезков,&lt;br /&gt;
поддерживающее 2 вида запросов: присвоение в точке и минимум на отрезке. Изначально сделаем так, чтобы дереву отрезков соответствовал массив &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;b_i = 0&amp;lt;/tex&amp;gt;, если в i-м городе принимает госпиталь и &amp;lt;tex&amp;gt;b_i = 1&amp;lt;/tex&amp;gt; иначе. Когда в каком-то городе открывается/закрывается госпиталь {{---}} делаем запрос на изменение в дереве отрезков. Когда требуется узнать ближайщий госпиталь к &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-му городу, можно воспользоваться одной из следующих идей: &lt;br /&gt;
а) (&amp;lt;tex&amp;gt;O(n \cdot log^2(n)&amp;lt;/tex&amp;gt;) Бинарным поиском ищем ближайший слева и ближайший справа к i-му городу госпиталь (такой город &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, что &amp;lt;tex&amp;gt;\min\limits_{k=i..j}b_k= 1&amp;lt;/tex&amp;gt;). Для этого внутри бинарного поиска каждый раз делаем запрос на поиск минимума в дереве отрезков.  &lt;br /&gt;
б) (&amp;lt;tex&amp;gt;O(n \cdot log(n)&amp;lt;/tex&amp;gt;) Будем одним спуском/подъемом по дереву определять, куда нам нужно идти (в левое или правое поддерево), тем самым делая одновоременно и бинарный поиск, и спуск/подъем по дереву.&lt;br /&gt;
&lt;br /&gt;
== Статическая центроидная декомпозиция ==&lt;br /&gt;
Перейдем к обобщению поставленных задач на случай дерева. Начнем, как и полагается, с первой:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть взвешенное дерево &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин, в каждой вершине которого написаны положительные целые числа. Также по-прежнему даны числа &amp;lt;tex&amp;gt;W &amp;gt;= 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; вершин дерева, таких что расстояние между ними не превосходит &amp;lt;math&amp;gt;l&amp;lt;/math&amp;gt; по числу ребер и не превосходит &amp;lt;math&amp;gt;W&amp;lt;/math&amp;gt; по сумме весов.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Для решения новой задачи применим ту же идею, что и была до этого {{---}} разделяй и властвуй. Для этого нам потребуется следующий объект :&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|id=191213&lt;br /&gt;
|definition =&lt;br /&gt;
'''Центроидом дерева''' (англ. ''centroid'') называется такая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, после удаления которой дерево разбивается на несколько (&amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;) поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, таких что для каждого &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;: &amp;lt;tex&amp;gt;|t_i| \leqslant \frac{n}{2}&amp;lt;/tex&amp;gt;, т.е. размер каждого поддерева не превосходит половины размера исходного дерева.&lt;br /&gt;
}}&lt;br /&gt;
Итак, в случае дерева идея разделяй-и-властвуй из предыдущего пункта будет формулироваться так: найдем центроид. Предположим, что мы сумели найти центроид за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; {{---}} размер дерева. Тогда, как и в упрощенной версии задачи {{---}} рекурсивно найдем ответ для всех поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, после чего попытаемся найти недостающие пары вершин, находящиехя в разных поддеревьях и удовлетворяющих вопросу задачи. Для этого будем отвечать на следующие запросы: пусть мы сейчас считаем все пары, где первая из вершин находится в поддереве &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и мы в некоторой структуре данных &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; храним все вершины остальных деревьев (каждую вершину задаем парой &amp;lt;math&amp;gt;(depth(v), length(v))&amp;lt;/math&amp;gt; {{---}} глубина вершины и длина пути до нее из корня поддерева), расстояние до которых от корня их поддерева не превышает &amp;lt;math&amp;gt;min(l, n)&amp;lt;/math&amp;gt;. Тогда просто пройдемся по всем вершинам &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; поддерева &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и прибавим к ответу число вершин в структуре &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt;, таких, что &amp;lt;tex&amp;gt;depth(u) \leqslant l - depth(v)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;length(u) \leqslant l - length(v)&amp;lt;/tex&amp;gt;. Это двумерные запросы, на которые можно отвечать за &amp;lt;math&amp;gt;O(log^2(n)&amp;lt;/math&amp;gt; с помощью [[Многомерное_дерево_отрезков|2d-дерева отрезков]], либо за &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с помощью [[Перечисление_точек_в_произвольном_прямоугольнике_за_n_*_log_%5E(d_-_1)_n_(range_tree)|техники поиска точек в d-мерном пространстве]]. Также читателю предлагается придумать и более эффективные и простые способы решить эту подзадачу.&lt;br /&gt;
&lt;br /&gt;
Оценим итоговую асимптотику: &amp;lt;tex&amp;gt;T(n) = k * T(n / k) + O(n \cdot log(n))&amp;lt;/tex&amp;gt;. Решая это рекурентное соотношение, получим &amp;lt;math&amp;gt;O(n \cdot log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь, как и было обещено, докажем лемму о существовании центроида и опишем алгоритм его эффективного поиска.&lt;br /&gt;
&lt;br /&gt;
=== Лемма о существовании центроида и алгоритм его нахождения. ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
В любом дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; существует центроид.&lt;br /&gt;
|proof=&lt;br /&gt;
Рассмотрим корень дерева &amp;lt;math&amp;gt;(r)&amp;lt;/math&amp;gt;. Положим изначально &amp;lt;math&amp;gt;v = r&amp;lt;/math&amp;gt;. Изначально &amp;lt;math&amp;gt;|subtree(v)| = n&amp;lt;/math&amp;gt;. Среди всех детей &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; выберем вершину &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; с максимальным размером поддерева. Если &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид, то положим &amp;lt;math&amp;gt;v = u&amp;lt;/math&amp;gt; и продолжим выбор нового u, иначе {{---}} остановимся. Докажем, что мы в какой-то момент остановимся. Пусть в призвольный момент времени &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид и размер её наддерева меньше &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, значит максимальное поддерево имеет размер больше чем &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, т.е. &amp;lt;math&amp;gt;|subtree(u)| &amp;gt; \frac{n}{2}&amp;lt;/math&amp;gt;, а значит размер &amp;quot;наддерева&amp;quot; вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; равен &amp;lt;tex&amp;gt;n - |subtree(u)| &amp;lt; \frac{n}{2}&amp;lt;/tex&amp;gt;. При этом теперь размер любого поддерева, на которое распадется дерево t при удалении вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; не превосходит &amp;lt;math&amp;gt;|subtree(u)| - 1&amp;lt;/math&amp;gt;, т.к. наддерево имеет размер меньше, чем поддерево &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, а любое поддерево вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; имеет хотя бы на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; вершину меньше (сама вершина &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;). По индукции получаем, что в любой момент времени размер наддерева вершины v меньше &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, значит мы будем спускаться только вниз по дереву &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, и при переходе к вершине &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} сыну &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; размер максимального поддерева уменьшится как минимум на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Значит не более чем за &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; шагов наши действия прекратятся и мы окажемся в центроиде дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
Итак, мы конструктивно доказали существование центроида и привели линейный относительно размера дерева алгоритм его нахождения. &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Реализация ===&lt;br /&gt;
&lt;br /&gt;
Поиск центроида в дереве:&lt;br /&gt;
&lt;br /&gt;
  '''int''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int''' sz[n])&lt;br /&gt;
     max_subtree = -1&lt;br /&gt;
     '''for''' u : children(v)&lt;br /&gt;
          '''if''' sz[u] &amp;gt; sz[v] / 2 and sz[u] &amp;gt; sz[max_subtree] &amp;lt;font color=green&amp;gt;// считаем sz[-1] = 0 &amp;lt;/font &amp;gt;&lt;br /&gt;
               max_subtree = u&lt;br /&gt;
     '''if''' max_subtree == -1&lt;br /&gt;
          return v&lt;br /&gt;
     '''else''' &lt;br /&gt;
          '''return''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz)&lt;br /&gt;
&lt;br /&gt;
Шаблон решения произвольной задачи на статическую центроидную декомпозицию:&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int''' sz[n])&lt;br /&gt;
     c = &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz) &amp;lt;font color=green&amp;gt;// находим c {{---}} центроид поддерева вершины v &amp;lt;/font &amp;gt;&lt;br /&gt;
     ch = children[c]&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{deleteEdges}&amp;lt;/tex&amp;gt;(c, children) &amp;lt;font color=green&amp;gt;// удаляем все ребра между вершиной с и детьми, чтобы мы не смогли из детей попасть в с. &lt;br /&gt;
         Это полезно делать, если решение подзадачи для поддерева предполагает проход dfs &amp;lt;/font &amp;gt;&lt;br /&gt;
     '''for''' c2 : ch[c]&lt;br /&gt;
          &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;(children, c2, sz)&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{mergeSolution}&amp;lt;/tex&amp;gt;(children, c2) &amp;lt;font color=green&amp;gt;// решаем для текущего поддерева &amp;lt;/font &amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Динамическая центроидная декомпозиция (дерево центроидной декомпозиции) ==&lt;br /&gt;
Теперь вернемся ко второй задаче введения. Для ее решения мы пользовались динамичекой версией divide &amp;amp; conquer {{---}} деревом отрезков. В предыдущем пункте мы определили статический аналог divide &amp;amp; conquer для дерева. Теперь обобщим этот метод для динамических задач.&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Деревом центроидов (или центроидной декомпозицией)''' дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; называют дерево &amp;lt;math&amp;gt;T(t)&amp;lt;/math&amp;gt;, построенное на вершинах дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; следующим образом :&lt;br /&gt;
* Пусть вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} центроид дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Объявим его корнем нового дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пусть при удалении вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; из дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; оно распалось на поддеревья &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, тогда детьми вершины c объявим &amp;lt;tex&amp;gt;T(t_1), T(t_t),\dots, T(t_k)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
=== Свойства центроидной декомпозиции ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
'''Свойства центроидной декомпозиции''' :&lt;br /&gt;
# Глубина дерева центроидов не превосходит &amp;lt;tex&amp;gt;log(n)&amp;lt;/tex&amp;gt;. &lt;br /&gt;
# Каждая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; является центроидом одного из поддеревьев дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждая вершина дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; поддеревьям дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (еще говорят, что вершина принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; центроидам дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, или что эти центроиды содержат вершину)&lt;br /&gt;
# Для любых вершин &amp;lt;tex&amp;gt;u, v \in T (u \neq v)&amp;lt;/tex&amp;gt; верно ровно одно из следующих трех утверждений: &lt;br /&gt;
a) &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
b) &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
c) &amp;lt;tex&amp;gt;T(u) \cap T(v) = \emptyset &amp;lt;/tex&amp;gt;&lt;br /&gt;
# Простой путь между любой парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; содержит центроид &amp;lt;tex&amp;gt;c \in T(t)&amp;lt;/tex&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
# Действительно, т.к. размер поддерева &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; не превосходит &amp;lt;tex&amp;gt;\frac{|subtree(c)|}{2}&amp;lt;/tex&amp;gt;, то при спуске в каждую следующую вершину на пути к любому листу в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; размер поддерева вершины, в которой мы сейчас находимся, уменьшается как минимум на &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;. Значит длина всего пути до листа не превосходит &amp;lt;math&amp;gt;log(n)&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
# Второе свойство очевидно из построения дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, т.к. если вершина принадлежит дереву центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то она является центроидом, а из построения &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы знаем, что каждая вершина исходного дерева принадлежит и дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Третье свойство {{---}} прямое следствие первых двух, т.к. вершина принадлежит любому центроиду &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; т.и т.т., когда c {{---}} отец вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов. Т.к. вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; точно принадлежит дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (свойство 2), то она лежит на каком-то пути в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, причем все ее родители (центроиды) ее содержат. А по свойству 1 длина любого вертикального (и даже простого) пути есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
# Четвертое свойство очевидно из того, что &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; {{---}} дерево. Т.к. &amp;lt;math&amp;gt;T(u)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;T(v)&amp;lt;/math&amp;gt; {{---}} поддеревья различных вершин дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то либо они не пересекаются, либо &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;, либо &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Для доказательства последнего свойства выберем в качестве вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;lca(u, v)&amp;lt;/math&amp;gt; в дереве центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Покажем, что так выбранная вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удовлетворяет заявленным свойствам. То, что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt; {{---}} очевидно по определению &amp;lt;math&amp;gt;lca&amp;lt;/math&amp;gt;, т.к. каждый предок любой вершины в дереве центроидов содержит эту вершину. Теперь докажем, что &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежит на пути между парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt;. Т.к. &amp;lt;math&amp;gt;c = lca(u, v)&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то из &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; нет ребра в такого сына, который содержит одновременно &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в своем поддереве (в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;), значит после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; разделится на несколько поддеревьев, таких что вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; окажутся в разных компонентах связности. А значит найдется такое ребро &amp;lt;math&amp;gt;(c, x)&amp;lt;/math&amp;gt;, которое принадлежало пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, но после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удалилось. Это доказывает то, что вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежала на пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
С помощью описанных свойств дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы можем решить задачу 2 для дерева :&lt;br /&gt;
&lt;br /&gt;
=== Пример решения задачи с помощью центроидной декомпозиции === &lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин. В каждый момент времени любая вершина дерева может быть либо помечена, либо нет. Изначально помечена только вершина номер &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Дан список из &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; запросов: &lt;br /&gt;
* Вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; пометили.&lt;br /&gt;
* С вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; сняли пометку.&lt;br /&gt;
* Найти номер ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
'''Решение :'''&lt;br /&gt;
&lt;br /&gt;
Построим центроидную декомпозицию &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Изначально посчитаем для каждого центроида, содержащего вершину &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt; посчитаем расстояние до вершины &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Для этого воспользуемся [[Метод_двоичного_подъема|методом двоичных подъемов для поиска lca пары вершин в дереве]], а также тем фактом, что если глубина &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве определена как расстояние от корня до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то длина пути между парой вершин &amp;lt;math&amp;gt;(u, v)&amp;lt;/math&amp;gt; есть &amp;lt;tex&amp;gt;len(u, v) = h(u) + h(v) - 2 \cdot h(lca(u, v))&amp;lt;/tex&amp;gt;. Если изначально предпосчитать проходом [[Обход_в_глубину,_цвета_вершин| dfs]] величины &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, то ответ на запрос &amp;lt;math&amp;gt;len(u, v)&amp;lt;/math&amp;gt; можно делать за время &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с &amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt;доп. памятью. Также можно воспользоваться техникой [[Сведение_задачи_LCA_к_задаче_RMQ|сведения задачи LCA к RMQ]] и решить с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; времени на запрос. Теперь научимся отвечать на запросы. Из последнего свойства центроидной декомпозиции видно, что если &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} искомая ближайшая помеченная вершина к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то путь между ними содержит центроид &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;, причем &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок одновременно вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве&amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Поэтому заведем [[Красно-черное_дерево|двоичное дерево поиска]] для каждого центроида &amp;lt;math&amp;gt;c \in T&amp;lt;/math&amp;gt;. В этой структуре для каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; будем хранить пары &amp;lt;math&amp;gt;(len(c, u), u)&amp;lt;/math&amp;gt; для всех помеченных вершин &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в поддереве центроидов &amp;lt;math&amp;gt;T(c)&amp;lt;/math&amp;gt;. Когда приходит запрос пометить вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} добавим в структуру данных для всех предков &amp;lt;math&amp;gt;p_c&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; пары &amp;lt;math&amp;gt;(len(p_c, v), v)&amp;lt;/math&amp;gt;. Мы совершим &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; добавлений, затратив &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; действий на каждое. Запрос снятия пометки с вершины обрабатывается аналогичными удалениями. Запрос поиска ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины {{---}} это запрос поиска вершины u, такой что  величина &amp;lt;math&amp;gt;len(c, u) + len(c, v)&amp;lt;/math&amp;gt; минимальна, где &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов (по пятому свойству, нас интересуют именно такие &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;). Этот запрос занимает так же &amp;lt;tex&amp;gt;O(log^2(n))&amp;lt;/tex&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
Итак, мы научились решать задачу с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и временной сложностью &amp;lt;math&amp;gt;O(log^2(n))&amp;lt;/math&amp;gt; на запрос любого типа с предварительным предпосчетом за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Варианты хранения центроидной декомпозиции ==&lt;br /&gt;
Для хранения дерева центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; есть &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; подхода, имеющих свои преимущества и недостатки :&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева запомним величину &amp;lt;math&amp;gt;p_v&amp;lt;/math&amp;gt; {{---}} номер предка вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Этот подход наиболее экономный по памяти (&amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;), но уступает в скорости и функциональности.&lt;br /&gt;
&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева будем хранить весь массив предков &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt; в дереве центроидов.&lt;br /&gt;
&lt;br /&gt;
Этот подход уступает в количестве необходимой дополнительной памяти (&amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt; суммарно), но имеет ряд преимуществ :&lt;br /&gt;
&lt;br /&gt;
#При проходе по массиву предков фиксированной вершины будет выигрыш в скорости работы, т.к. весь массив будет лежать непрерывным блоком данных и следовательно будет закэширован&lt;br /&gt;
# На массиве предков можно строить различные структуры данных (такие как, например, дерево отрезков) для быстрого (в случае с деревом отрезков &amp;lt;math&amp;gt;O(log(log(n)))&amp;lt;/math&amp;gt; на запрос) поиска предка с необходимыми свойствами. Так, например, в описанной выше задаче про помеченные вершины наибольшего общего предка можно искать методом двоичных подъемов за &amp;lt;math&amp;gt;O(log(log(n))&amp;lt;/math&amp;gt; на запрос, т.к. размер массива предков есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; (по свойству 1 центроидной декомпозиции). Используя эту оптимизацию можно получить время &amp;lt;math&amp;gt;O(log(n)) + O(log(log(n)) = O(log(n))&amp;lt;/math&amp;gt; на запрос нахождения ближайшей помеченной вершины. Чтобы добиться улучшенной асимптотики для запросов изменения можно хранить дерево отрезков на каждом из путей &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt;, в каждой вершине которого хранить двоичное дерево поиска и поддерживать отложенные операции. Тогда ответ на эти запросы будет занимать &amp;lt;math&amp;gt;O(log(n) \cdot log(log(n))&amp;lt;/math&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
&lt;br /&gt;
*[[:Дерево_отрезков._Построение|Дерево отрезков]]&lt;br /&gt;
*[[:Heavy-light_декомпозиция|Heavy-light декомпозиция]]&lt;br /&gt;
*[[:Метод_двоичного_подъема| Метод двоичного подъема]]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы и структуры данных]]&lt;/div&gt;</summary>
		<author><name>92.255.113.52</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61632</id>
		<title>Centroid decomposition</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61632"/>
				<updated>2017-06-14T21:41:46Z</updated>
		
		<summary type="html">&lt;p&gt;92.255.113.52: /* Свойства центроидной декомпозиции */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Centroid decomposition (рус. центроидная декомпозиция) {{---}} это структура данных, позволяющая отвечать на запросы на дереве. Чаще всего это запросы, связанные с нахождением функции на вершинах, связанных неравенством на расстояние между ними в дереве. Также иногда применяется для запросов на путях в дереве.&lt;br /&gt;
&lt;br /&gt;
== Введение ==&lt;br /&gt;
Рассмотрим &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; задачи на обычном массиве (в дальнейшем мы будем их обобщать на случай дерева):&lt;br /&gt;
&lt;br /&gt;
Задача &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть массив &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; положительных целых чисел из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов. Также дано число &amp;lt;tex&amp;gt;W \geqslant 0&amp;lt;/tex&amp;gt; и число &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; индексов массива, таких что &amp;lt;tex&amp;gt;|j - i| \leqslant l &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\sum_{i=0}^{n - 1} a_i \leqslant W&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
Задача &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть прямая дорога, на которой расположены &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; городов. В некоторых городах есть госпитали, которые могут принимать больных. Поступают запросы вида :&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;, в котором находится больной и требуется найти такой город &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;|u - v|&amp;lt;/tex&amp;gt; минимально возможное.&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что больше он не будет принимать больных&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что теперь он может принимать больных&lt;br /&gt;
}}&lt;br /&gt;
Для начала решим обе задачи. &lt;br /&gt;
Первая задача решается методом [[Сортировка_слиянием|divide &amp;amp; conquer (рус. разделяй и властвуй)]] {{---}} давайте разделим массив &amp;lt;tex&amp;gt;a[0 \dots n-1]&amp;lt;/tex&amp;gt; на 2 массива &amp;lt;tex&amp;gt;a[0\dots\frac{n}{2} - 1]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;a[\frac{n}{2} \dots n-1]&amp;lt;/tex&amp;gt; и рекурсивно решим задачу для каждого из них. Осталось научиться находить количество искомых пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt;, таких что &amp;lt;tex&amp;gt;i &amp;lt; \frac{n}{2}, j \geqslant \frac{n}{2}&amp;lt;/tex&amp;gt;. Для этого воспользуемся другой известной техникой {{---}} методом двух указателей. Посчитаем массив префиксных сумм для правой половины &amp;lt;tex&amp;gt;pref[i] = \sum_{j=\frac{n}{2}}^{i} a_j&amp;lt;/tex&amp;gt; и суффиксных (&amp;lt;tex&amp;gt;suf[i] = \sum_{j=i}^{\frac{n}{2} + 1} a_j&amp;lt;/tex&amp;gt;) {{---}} для левой. Заведем два указателя (&amp;lt;tex&amp;gt;p_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;p_2&amp;lt;/tex&amp;gt;). Изначально установим &amp;lt;tex&amp;gt;p_1 = \frac{n}{2} - l + 1, p_2 = \frac{n}{2}&amp;lt;/tex&amp;gt;. Пока &amp;lt;tex&amp;gt;p_2 - 1&amp;gt; \frac{n}{2}&amp;lt;/tex&amp;gt; и&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;pref[p_2] + suf[p_1] &amp;gt; W &amp;lt;/tex&amp;gt; будем уменьшать &amp;lt;math&amp;gt;p_2&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Если после этого &amp;lt;math&amp;gt;pref[p_2] + suf[p_1] \leqslant W&amp;lt;/math&amp;gt;, то к ответу прибавим &amp;lt;math&amp;gt;(p_2 - \frac{n}{2} + 1) * (\frac{n}{2} - p_1)&amp;lt;/math&amp;gt;, посго, увеличим &amp;lt;math&amp;gt;p_1&amp;lt;/math&amp;gt; на &amp;lt;math/math&amp;gt;. Так будем делать, пока &amp;lt;math&amp;gt;p_1 &amp;lt; \frac{n}{2}&amp;lt;/math&amp;gt;. В конце сложим текущий ответ и ответы для половин массива {{---}} получим ответ на задачу. Асимптотика такого алгоритма: &amp;lt;tex&amp;gt;T(n) = 2 * T(n / 2) + O(n) = O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вторая задача имеет запросы на изменение и поэтому надо применить динамическую версию divide &amp;amp; conquer {{---}} [[Дерево_отрезков._Построение|дерево отрезков]]. Построим дерево отрезков,&lt;br /&gt;
поддерживающее 2 вида запросов: присвоение в точке и минимум на отрезке. Изначально сделаем так, чтобы дереву отрезков соответствовал массив &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;b_i = 0&amp;lt;/tex&amp;gt;, если в i-м городе принимает госпиталь и &amp;lt;tex&amp;gt;b_i = 1&amp;lt;/tex&amp;gt; иначе. Когда в каком-то городе открывается/закрывается госпиталь {{---}} делаем запрос на изменение в дереве отрезков. Когда требуется узнать ближайщий госпиталь к &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-му городу, можно воспользоваться одной из следующих идей: &lt;br /&gt;
а) (&amp;lt;tex&amp;gt;O(n \cdot log^2(n)&amp;lt;/tex&amp;gt;) Бинарным поиском ищем ближайший слева и ближайший справа к i-му городу госпиталь (такой город &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, что &amp;lt;tex&amp;gt;\min\limits_{k=i..j}b_k= 1&amp;lt;/tex&amp;gt;). Для этого внутри бинарного поиска каждый раз делаем запрос на поиск минимума в дереве отрезков.  &lt;br /&gt;
б) (&amp;lt;tex&amp;gt;O(n \cdot log(n)&amp;lt;/tex&amp;gt;) Будем одним спуском/подъемом по дереву определять, куда нам нужно идти (в левое или правое поддерево), тем самым делая одновоременно и бинарный поиск, и спуск/подъем по дереву.&lt;br /&gt;
&lt;br /&gt;
== Статическая центроидная декомпозиция ==&lt;br /&gt;
Перейдем к обобщению поставленных задач на случай дерева. Начнем, как и полагается, с первой:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть взвешенное дерево &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин, в каждой вершине которого написаны положительные целые числа. Также по-прежнему даны числа &amp;lt;tex&amp;gt;W &amp;gt;= 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; вершин дерева, таких что расстояние между ними не превосходит &amp;lt;math&amp;gt;l&amp;lt;/math&amp;gt; по числу ребер и не превосходит &amp;lt;math&amp;gt;W&amp;lt;/math&amp;gt; по сумме весов.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Для решения новой задачи применим ту же идею, что и была до этого {{---}} разделяй и властвуй. Для этого нам потребуется следующий объект :&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|id=191213&lt;br /&gt;
|definition =&lt;br /&gt;
'''Центроидом дерева''' (англ. ''centroid'') называется такая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, после удаления которой дерево разбивается на несколько (&amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;) поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, таких что для каждого &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;: &amp;lt;tex&amp;gt;|t_i| \leqslant \frac{n}{2}&amp;lt;/tex&amp;gt;, т.е. размер каждого поддерева не превосходит половины размера исходного дерева.&lt;br /&gt;
}}&lt;br /&gt;
Итак, в случае дерева идея разделяй-и-властвуй из предыдущего пункта будет формулироваться так: найдем центроид. Предположим, что мы сумели найти центроид за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; {{---}} размер дерева. Тогда, как и в упрощенной версии задачи {{---}} рекурсивно найдем ответ для всех поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, после чего попытаемся найти недостающие пары вершин, находящиехя в разных поддеревьях и удовлетворяющих вопросу задачи. Для этого будем отвечать на следующие запросы: пусть мы сейчас считаем все пары, где первая из вершин находится в поддереве &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и мы в некоторой структуре данных &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; храним все вершины остальных деревьев (каждую вершину задаем парой &amp;lt;math&amp;gt;(depth(v), length(v))&amp;lt;/math&amp;gt; {{---}} глубина вершины и длина пути до нее из корня поддерева), расстояние до которых от корня их поддерева не превышает &amp;lt;math&amp;gt;min(l, n)&amp;lt;/math&amp;gt;. Тогда просто пройдемся по всем вершинам &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; поддерева &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и прибавим к ответу число вершин в структуре &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt;, таких, что &amp;lt;tex&amp;gt;depth(u) \leqslant l - depth(v)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;length(u) \leqslant l - length(v)&amp;lt;/tex&amp;gt;. Это двумерные запросы, на которые можно отвечать за &amp;lt;math&amp;gt;O(log^2(n)&amp;lt;/math&amp;gt; с помощью [[Многомерное_дерево_отрезков|2d-дерева отрезков]], либо за &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с помощью [[Перечисление_точек_в_произвольном_прямоугольнике_за_n_*_log_%5E(d_-_1)_n_(range_tree)|техники поиска точек в d-мерном пространстве]]. Также читателю предлагается придумать и более эффективные и простые способы решить эту подзадачу.&lt;br /&gt;
&lt;br /&gt;
Оценим итоговую асимптотику: &amp;lt;tex&amp;gt;T(n) = k * T(n / k) + O(n \cdot log(n))&amp;lt;/tex&amp;gt;. Решая это рекурентное соотношение, получим &amp;lt;math&amp;gt;O(n \cdot log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь, как и было обещено, докажем лемму о существовании центроида и опишем алгоритм его эффективного поиска.&lt;br /&gt;
&lt;br /&gt;
=== Лемма о существовании центроида и алгоритм его нахождения. ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
В любом дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; существует центроид.&lt;br /&gt;
|proof=&lt;br /&gt;
Рассмотрим корень дерева &amp;lt;math&amp;gt;(r)&amp;lt;/math&amp;gt;. Положим изначально &amp;lt;math&amp;gt;v = r&amp;lt;/math&amp;gt;. Изначально &amp;lt;math&amp;gt;|subtree(v)| = n&amp;lt;/math&amp;gt;. Среди всех детей &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; выберем вершину &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; с максимальным размером поддерева. Если &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид, то положим &amp;lt;math&amp;gt;v = u&amp;lt;/math&amp;gt; и продолжим выбор нового u, иначе {{---}} остановимся. Докажем, что мы в какой-то момент остановимся. Пусть в призвольный момент времени &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид и размер её наддерева меньше &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, значит максимальное поддерево имеет размер больше чем &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, т.е. &amp;lt;math&amp;gt;|subtree(u)| &amp;gt; \frac{n}{2}&amp;lt;/math&amp;gt;, а значит размер &amp;quot;наддерева&amp;quot; вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; равен &amp;lt;tex&amp;gt;n - |subtree(u)| &amp;lt; \frac{n}{2}&amp;lt;/tex&amp;gt;. При этом теперь размер любого поддерева, на которое распадется дерево t при удалении вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; не превосходит &amp;lt;math&amp;gt;|subtree(u)| - 1&amp;lt;/math&amp;gt;, т.к. наддерево имеет размер меньше, чем поддерево &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, а любое поддерево вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; имеет хотя бы на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; вершину меньше (сама вершина &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;). По индукции получаем, что в любой момент времени размер наддерева вершины v меньше &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, значит мы будем спускаться только вниз по дереву &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, и при переходе к вершине &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} сыну &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; размер максимального поддерева уменьшится как минимум на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Значит не более чем за &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; шагов наши действия прекратятся и мы окажемся в центроиде дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
Итак, мы конструктивно доказали существование центроида и привели линейный относительно размера дерева алгоритм его нахождения. &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Реализация ===&lt;br /&gt;
&lt;br /&gt;
Поиск центроида в дереве:&lt;br /&gt;
&lt;br /&gt;
  '''int''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int''' sz[n])&lt;br /&gt;
     max_subtree = -1&lt;br /&gt;
     '''for''' u : children(v)&lt;br /&gt;
          '''if''' sz[u] &amp;gt; sz[v] / 2 and sz[u] &amp;gt; sz[max_subtree] &amp;lt;font color=green&amp;gt;// считаем sz[-1] = 0 &amp;lt;/font &amp;gt;&lt;br /&gt;
               max_subtree = u&lt;br /&gt;
     '''if''' max_subtree == -1&lt;br /&gt;
          return v&lt;br /&gt;
     '''else''' &lt;br /&gt;
          '''return''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz)&lt;br /&gt;
&lt;br /&gt;
Шаблон решения произвольной задачи на статическую центроидную декомпозицию:&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int''' sz[n])&lt;br /&gt;
     c = &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz) &amp;lt;font color=green&amp;gt;// находим c {{---}} центроид поддерева вершины v &amp;lt;/font &amp;gt;&lt;br /&gt;
     ch = children[c]&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{deleteEdges}&amp;lt;/tex&amp;gt;(c, children) &amp;lt;font color=green&amp;gt;// удаляем все ребра между вершиной с и детьми, чтобы мы не смогли из детей попасть в с. &lt;br /&gt;
         Это полезно делать, если решение подзадачи для поддерева предполагает проход dfs &amp;lt;/font &amp;gt;&lt;br /&gt;
     '''for''' c2 : ch[c]&lt;br /&gt;
          &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;(children, c2, sz)&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{mergeSolution}&amp;lt;/tex&amp;gt;(children, c2) &amp;lt;font color=green&amp;gt;// решаем для текущего поддерева &amp;lt;/font &amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Динамическая центроидная декомпозиция (дерево центроидной декомпозиции) ==&lt;br /&gt;
Теперь вернемся ко второй задаче введения. Для ее решения мы пользовались динамичекой версией divide &amp;amp; conquer {{---}} деревом отрезков. В предыдущем пункте мы определили статический аналог divide &amp;amp; conquer для дерева. Теперь обобщим этот метод для динамических задач.&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Деревом центроидов (или центроидной декомпозицией)''' дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; называют дерево &amp;lt;math&amp;gt;T(t)&amp;lt;/math&amp;gt;, построенное на вершинах дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; следующим образом :&lt;br /&gt;
* Пусть вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} центроид дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Объявим его корнем нового дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пусть при удалении вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; из дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; оно распалось на поддеревья &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, тогда детьми вершины c объявим &amp;lt;tex&amp;gt;T(t_1), T(t_t),\dots, T(t_k)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
=== Свойства центроидной декомпозиции ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
'''Свойства центроидной декомпозиции''' :&lt;br /&gt;
# Глубина дерева центроидов не превосходит &amp;lt;tex&amp;gt;log(n)&amp;lt;/tex&amp;gt;. &lt;br /&gt;
# Каждая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; является центроидом одного из поддеревьев дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждая вершина дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; поддеревьям дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (еще говорят, что вершина принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; центроидам дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, или что эти центроиды содержат вершину)&lt;br /&gt;
# Для любых вершин &amp;lt;tex&amp;gt;u, v \in T (u \neq v)&amp;lt;/tex&amp;gt; верно ровно одно из следующих трех утверждений: &lt;br /&gt;
 a) &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 b) &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 c) &amp;lt;tex&amp;gt;T(u) \cap T(v) = \emptyset &amp;lt;/tex&amp;gt;&lt;br /&gt;
# Простой путь между любой парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; содержит центроид &amp;lt;tex&amp;gt;c \in T(t)&amp;lt;/tex&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
# Действительно, т.к. размер поддерева &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; не превосходит &amp;lt;tex&amp;gt;\frac{|subtree(c)|}{2}&amp;lt;/tex&amp;gt;, то при спуске в каждую следующую вершину на пути к любому листу в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; размер поддерева вершины, в которой мы сейчас находимся, уменьшается как минимум на &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;. Значит длина всего пути до листа не превосходит &amp;lt;math&amp;gt;log(n)&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
# Второе свойство очевидно из построения дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, т.к. если вершина принадлежит дереву центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то она является центроидом, а из построения &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы знаем, что каждая вершина исходного дерева принадлежит и дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Третье свойство {{---}} прямое следствие первых двух, т.к. вершина принадлежит любому центроиду &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; т.и т.т., когда c {{---}} отец вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов. Т.к. вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; точно принадлежит дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (свойство 2), то она лежит на каком-то пути в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, причем все ее родители (центроиды) ее содержат. А по свойству 1 длина любого вертикального (и даже простого) пути есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
# Четвертое свойство очевидно из того, что &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; {{---}} дерево. Т.к. &amp;lt;math&amp;gt;T(u)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;T(v)&amp;lt;/math&amp;gt; {{---}} поддеревья различных вершин дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то либо они не пересекаются, либо &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;, либо &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Для доказательства последнего свойства выберем в качестве вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;lca(u, v)&amp;lt;/math&amp;gt; в дереве центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Покажем, что так выбранная вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удовлетворяет заявленным свойствам. То, что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt; {{---}} очевидно по определению &amp;lt;math&amp;gt;lca&amp;lt;/math&amp;gt;, т.к. каждый предок любой вершины в дереве центроидов содержит эту вершину. Теперь докажем, что &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежит на пути между парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt;. Т.к. &amp;lt;math&amp;gt;c = lca(u, v)&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то из &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; нет ребра в такого сына, который содержит одновременно &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в своем поддереве (в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;), значит после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; разделится на несколько поддеревьев, таких что вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; окажутся в разных компонентах связности. А значит найдется такое ребро &amp;lt;math&amp;gt;(c, x)&amp;lt;/math&amp;gt;, которое принадлежало пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, но после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удалилось. Это доказывает то, что вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежала на пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
С помощью описанных свойств дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы можем решить задачу 2 для дерева :&lt;br /&gt;
&lt;br /&gt;
=== Пример решения задачи с помощью центроидной декомпозиции === &lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин. В каждый момент времени любая вершина дерева может быть либо помечена, либо нет. Изначально помечена только вершина номер &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Дан список из &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; запросов: &lt;br /&gt;
* Вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; пометили.&lt;br /&gt;
* С вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; сняли пометку.&lt;br /&gt;
* Найти номер ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
'''Решение :'''&lt;br /&gt;
&lt;br /&gt;
Построим центроидную декомпозицию &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Изначально посчитаем для каждого центроида, содержащего вершину &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt; посчитаем расстояние до вершины &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Для этого воспользуемся [[Метод_двоичного_подъема|методом двоичных подъемов для поиска lca пары вершин в дереве]], а также тем фактом, что если глубина &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве определена как расстояние от корня до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то длина пути между парой вершин &amp;lt;math&amp;gt;(u, v)&amp;lt;/math&amp;gt; есть &amp;lt;tex&amp;gt;len(u, v) = h(u) + h(v) - 2 \cdot h(lca(u, v))&amp;lt;/tex&amp;gt;. Если изначально предпосчитать проходом [[Обход_в_глубину,_цвета_вершин| dfs]] величины &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, то ответ на запрос &amp;lt;math&amp;gt;len(u, v)&amp;lt;/math&amp;gt; можно делать за время &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с &amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt;доп. памятью. Также можно воспользоваться техникой [[Сведение_задачи_LCA_к_задаче_RMQ|сведения задачи LCA к RMQ]] и решить с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; времени на запрос. Теперь научимся отвечать на запросы. Из последнего свойства центроидной декомпозиции видно, что если &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} искомая ближайшая помеченная вершина к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то путь между ними содержит центроид &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;, причем &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок одновременно вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве&amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Поэтому заведем [[Красно-черное_дерево|двоичное дерево поиска]] для каждого центроида &amp;lt;math&amp;gt;c \in T&amp;lt;/math&amp;gt;. В этой структуре для каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; будем хранить пары &amp;lt;math&amp;gt;(len(c, u), u)&amp;lt;/math&amp;gt; для всех помеченных вершин &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в поддереве центроидов &amp;lt;math&amp;gt;T(c)&amp;lt;/math&amp;gt;. Когда приходит запрос пометить вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} добавим в структуру данных для всех предков &amp;lt;math&amp;gt;p_c&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; пары &amp;lt;math&amp;gt;(len(p_c, v), v)&amp;lt;/math&amp;gt;. Мы совершим &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; добавлений, затратив &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; действий на каждое. Запрос снятия пометки с вершины обрабатывается аналогичными удалениями. Запрос поиска ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины {{---}} это запрос поиска вершины u, такой что  величина &amp;lt;math&amp;gt;len(c, u) + len(c, v)&amp;lt;/math&amp;gt; минимальна, где &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов (по пятому свойству, нас интересуют именно такие &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;). Этот запрос занимает так же &amp;lt;tex&amp;gt;O(log^2(n))&amp;lt;/tex&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
Итак, мы научились решать задачу с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и временной сложностью &amp;lt;math&amp;gt;O(log^2(n))&amp;lt;/math&amp;gt; на запрос любого типа с предварительным предпосчетом за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Варианты хранения центроидной декомпозиции ==&lt;br /&gt;
Для хранения дерева центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; есть &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; подхода, имеющих свои преимущества и недостатки :&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева запомним величину &amp;lt;math&amp;gt;p_v&amp;lt;/math&amp;gt; {{---}} номер предка вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Этот подход наиболее экономный по памяти (&amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;), но уступает в скорости и функциональности.&lt;br /&gt;
&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева будем хранить весь массив предков &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt; в дереве центроидов.&lt;br /&gt;
&lt;br /&gt;
Этот подход уступает в количестве необходимой дополнительной памяти (&amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt; суммарно), но имеет ряд преимуществ :&lt;br /&gt;
&lt;br /&gt;
#При проходе по массиву предков фиксированной вершины будет выигрыш в скорости работы, т.к. весь массив будет лежать непрерывным блоком данных и следовательно будет закэширован&lt;br /&gt;
# На массиве предков можно строить различные структуры данных (такие как, например, дерево отрезков) для быстрого (в случае с деревом отрезков &amp;lt;math&amp;gt;O(log(log(n)))&amp;lt;/math&amp;gt; на запрос) поиска предка с необходимыми свойствами. Так, например, в описанной выше задаче про помеченные вершины наибольшего общего предка можно искать методом двоичных подъемов за &amp;lt;math&amp;gt;O(log(log(n))&amp;lt;/math&amp;gt; на запрос, т.к. размер массива предков есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; (по свойству 1 центроидной декомпозиции). Используя эту оптимизацию можно получить время &amp;lt;math&amp;gt;O(log(n)) + O(log(log(n)) = O(log(n))&amp;lt;/math&amp;gt; на запрос нахождения ближайшей помеченной вершины. Чтобы добиться улучшенной асимптотики для запросов изменения можно хранить дерево отрезков на каждом из путей &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt;, в каждой вершине которого хранить двоичное дерево поиска и поддерживать отложенные операции. Тогда ответ на эти запросы будет занимать &amp;lt;math&amp;gt;O(log(n) \cdot log(log(n))&amp;lt;/math&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
&lt;br /&gt;
*[[:Дерево_отрезков._Построение|Дерево отрезков]]&lt;br /&gt;
*[[:Heavy-light_декомпозиция|Heavy-light декомпозиция]]&lt;br /&gt;
*[[:Метод_двоичного_подъема| Метод двоичного подъема]]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы и структуры данных]]&lt;/div&gt;</summary>
		<author><name>92.255.113.52</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61631</id>
		<title>Centroid decomposition</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61631"/>
				<updated>2017-06-14T21:40:22Z</updated>
		
		<summary type="html">&lt;p&gt;92.255.113.52: /* Варианты хранения центроидной декомпозиции */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Centroid decomposition (рус. центроидная декомпозиция) {{---}} это структура данных, позволяющая отвечать на запросы на дереве. Чаще всего это запросы, связанные с нахождением функции на вершинах, связанных неравенством на расстояние между ними в дереве. Также иногда применяется для запросов на путях в дереве.&lt;br /&gt;
&lt;br /&gt;
== Введение ==&lt;br /&gt;
Рассмотрим &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; задачи на обычном массиве (в дальнейшем мы будем их обобщать на случай дерева):&lt;br /&gt;
&lt;br /&gt;
Задача &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть массив &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; положительных целых чисел из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов. Также дано число &amp;lt;tex&amp;gt;W \geqslant 0&amp;lt;/tex&amp;gt; и число &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; индексов массива, таких что &amp;lt;tex&amp;gt;|j - i| \leqslant l &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\sum_{i=0}^{n - 1} a_i \leqslant W&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
Задача &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть прямая дорога, на которой расположены &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; городов. В некоторых городах есть госпитали, которые могут принимать больных. Поступают запросы вида :&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;, в котором находится больной и требуется найти такой город &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;|u - v|&amp;lt;/tex&amp;gt; минимально возможное.&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что больше он не будет принимать больных&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что теперь он может принимать больных&lt;br /&gt;
}}&lt;br /&gt;
Для начала решим обе задачи. &lt;br /&gt;
Первая задача решается методом [[Сортировка_слиянием|divide &amp;amp; conquer (рус. разделяй и властвуй)]] {{---}} давайте разделим массив &amp;lt;tex&amp;gt;a[0 \dots n-1]&amp;lt;/tex&amp;gt; на 2 массива &amp;lt;tex&amp;gt;a[0\dots\frac{n}{2} - 1]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;a[\frac{n}{2} \dots n-1]&amp;lt;/tex&amp;gt; и рекурсивно решим задачу для каждого из них. Осталось научиться находить количество искомых пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt;, таких что &amp;lt;tex&amp;gt;i &amp;lt; \frac{n}{2}, j \geqslant \frac{n}{2}&amp;lt;/tex&amp;gt;. Для этого воспользуемся другой известной техникой {{---}} методом двух указателей. Посчитаем массив префиксных сумм для правой половины &amp;lt;tex&amp;gt;pref[i] = \sum_{j=\frac{n}{2}}^{i} a_j&amp;lt;/tex&amp;gt; и суффиксных (&amp;lt;tex&amp;gt;suf[i] = \sum_{j=i}^{\frac{n}{2} + 1} a_j&amp;lt;/tex&amp;gt;) {{---}} для левой. Заведем два указателя (&amp;lt;tex&amp;gt;p_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;p_2&amp;lt;/tex&amp;gt;). Изначально установим &amp;lt;tex&amp;gt;p_1 = \frac{n}{2} - l + 1, p_2 = \frac{n}{2}&amp;lt;/tex&amp;gt;. Пока &amp;lt;tex&amp;gt;p_2 - 1&amp;gt; \frac{n}{2}&amp;lt;/tex&amp;gt; и&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;pref[p_2] + suf[p_1] &amp;gt; W &amp;lt;/tex&amp;gt; будем уменьшать &amp;lt;math&amp;gt;p_2&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Если после этого &amp;lt;math&amp;gt;pref[p_2] + suf[p_1] \leqslant W&amp;lt;/math&amp;gt;, то к ответу прибавим &amp;lt;math&amp;gt;(p_2 - \frac{n}{2} + 1) * (\frac{n}{2} - p_1)&amp;lt;/math&amp;gt;, посго, увеличим &amp;lt;math&amp;gt;p_1&amp;lt;/math&amp;gt; на &amp;lt;math/math&amp;gt;. Так будем делать, пока &amp;lt;math&amp;gt;p_1 &amp;lt; \frac{n}{2}&amp;lt;/math&amp;gt;. В конце сложим текущий ответ и ответы для половин массива {{---}} получим ответ на задачу. Асимптотика такого алгоритма: &amp;lt;tex&amp;gt;T(n) = 2 * T(n / 2) + O(n) = O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вторая задача имеет запросы на изменение и поэтому надо применить динамическую версию divide &amp;amp; conquer {{---}} [[Дерево_отрезков._Построение|дерево отрезков]]. Построим дерево отрезков,&lt;br /&gt;
поддерживающее 2 вида запросов: присвоение в точке и минимум на отрезке. Изначально сделаем так, чтобы дереву отрезков соответствовал массив &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;b_i = 0&amp;lt;/tex&amp;gt;, если в i-м городе принимает госпиталь и &amp;lt;tex&amp;gt;b_i = 1&amp;lt;/tex&amp;gt; иначе. Когда в каком-то городе открывается/закрывается госпиталь {{---}} делаем запрос на изменение в дереве отрезков. Когда требуется узнать ближайщий госпиталь к &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-му городу, можно воспользоваться одной из следующих идей: &lt;br /&gt;
а) (&amp;lt;tex&amp;gt;O(n \cdot log^2(n)&amp;lt;/tex&amp;gt;) Бинарным поиском ищем ближайший слева и ближайший справа к i-му городу госпиталь (такой город &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, что &amp;lt;tex&amp;gt;\min\limits_{k=i..j}b_k= 1&amp;lt;/tex&amp;gt;). Для этого внутри бинарного поиска каждый раз делаем запрос на поиск минимума в дереве отрезков.  &lt;br /&gt;
б) (&amp;lt;tex&amp;gt;O(n \cdot log(n)&amp;lt;/tex&amp;gt;) Будем одним спуском/подъемом по дереву определять, куда нам нужно идти (в левое или правое поддерево), тем самым делая одновоременно и бинарный поиск, и спуск/подъем по дереву.&lt;br /&gt;
&lt;br /&gt;
== Статическая центроидная декомпозиция ==&lt;br /&gt;
Перейдем к обобщению поставленных задач на случай дерева. Начнем, как и полагается, с первой:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть взвешенное дерево &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин, в каждой вершине которого написаны положительные целые числа. Также по-прежнему даны числа &amp;lt;tex&amp;gt;W &amp;gt;= 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; вершин дерева, таких что расстояние между ними не превосходит &amp;lt;math&amp;gt;l&amp;lt;/math&amp;gt; по числу ребер и не превосходит &amp;lt;math&amp;gt;W&amp;lt;/math&amp;gt; по сумме весов.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Для решения новой задачи применим ту же идею, что и была до этого {{---}} разделяй и властвуй. Для этого нам потребуется следующий объект :&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|id=191213&lt;br /&gt;
|definition =&lt;br /&gt;
'''Центроидом дерева''' (англ. ''centroid'') называется такая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, после удаления которой дерево разбивается на несколько (&amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;) поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, таких что для каждого &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;: &amp;lt;tex&amp;gt;|t_i| \leqslant \frac{n}{2}&amp;lt;/tex&amp;gt;, т.е. размер каждого поддерева не превосходит половины размера исходного дерева.&lt;br /&gt;
}}&lt;br /&gt;
Итак, в случае дерева идея разделяй-и-властвуй из предыдущего пункта будет формулироваться так: найдем центроид. Предположим, что мы сумели найти центроид за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; {{---}} размер дерева. Тогда, как и в упрощенной версии задачи {{---}} рекурсивно найдем ответ для всех поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, после чего попытаемся найти недостающие пары вершин, находящиехя в разных поддеревьях и удовлетворяющих вопросу задачи. Для этого будем отвечать на следующие запросы: пусть мы сейчас считаем все пары, где первая из вершин находится в поддереве &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и мы в некоторой структуре данных &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; храним все вершины остальных деревьев (каждую вершину задаем парой &amp;lt;math&amp;gt;(depth(v), length(v))&amp;lt;/math&amp;gt; {{---}} глубина вершины и длина пути до нее из корня поддерева), расстояние до которых от корня их поддерева не превышает &amp;lt;math&amp;gt;min(l, n)&amp;lt;/math&amp;gt;. Тогда просто пройдемся по всем вершинам &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; поддерева &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и прибавим к ответу число вершин в структуре &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt;, таких, что &amp;lt;tex&amp;gt;depth(u) \leqslant l - depth(v)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;length(u) \leqslant l - length(v)&amp;lt;/tex&amp;gt;. Это двумерные запросы, на которые можно отвечать за &amp;lt;math&amp;gt;O(log^2(n)&amp;lt;/math&amp;gt; с помощью [[Многомерное_дерево_отрезков|2d-дерева отрезков]], либо за &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с помощью [[Перечисление_точек_в_произвольном_прямоугольнике_за_n_*_log_%5E(d_-_1)_n_(range_tree)|техники поиска точек в d-мерном пространстве]]. Также читателю предлагается придумать и более эффективные и простые способы решить эту подзадачу.&lt;br /&gt;
&lt;br /&gt;
Оценим итоговую асимптотику: &amp;lt;tex&amp;gt;T(n) = k * T(n / k) + O(n \cdot log(n))&amp;lt;/tex&amp;gt;. Решая это рекурентное соотношение, получим &amp;lt;math&amp;gt;O(n \cdot log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь, как и было обещено, докажем лемму о существовании центроида и опишем алгоритм его эффективного поиска.&lt;br /&gt;
&lt;br /&gt;
=== Лемма о существовании центроида и алгоритм его нахождения. ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
В любом дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; существует центроид.&lt;br /&gt;
|proof=&lt;br /&gt;
Рассмотрим корень дерева &amp;lt;math&amp;gt;(r)&amp;lt;/math&amp;gt;. Положим изначально &amp;lt;math&amp;gt;v = r&amp;lt;/math&amp;gt;. Изначально &amp;lt;math&amp;gt;|subtree(v)| = n&amp;lt;/math&amp;gt;. Среди всех детей &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; выберем вершину &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; с максимальным размером поддерева. Если &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид, то положим &amp;lt;math&amp;gt;v = u&amp;lt;/math&amp;gt; и продолжим выбор нового u, иначе {{---}} остановимся. Докажем, что мы в какой-то момент остановимся. Пусть в призвольный момент времени &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид и размер её наддерева меньше &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, значит максимальное поддерево имеет размер больше чем &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, т.е. &amp;lt;math&amp;gt;|subtree(u)| &amp;gt; \frac{n}{2}&amp;lt;/math&amp;gt;, а значит размер &amp;quot;наддерева&amp;quot; вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; равен &amp;lt;tex&amp;gt;n - |subtree(u)| &amp;lt; \frac{n}{2}&amp;lt;/tex&amp;gt;. При этом теперь размер любого поддерева, на которое распадется дерево t при удалении вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; не превосходит &amp;lt;math&amp;gt;|subtree(u)| - 1&amp;lt;/math&amp;gt;, т.к. наддерево имеет размер меньше, чем поддерево &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, а любое поддерево вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; имеет хотя бы на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; вершину меньше (сама вершина &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;). По индукции получаем, что в любой момент времени размер наддерева вершины v меньше &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, значит мы будем спускаться только вниз по дереву &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, и при переходе к вершине &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} сыну &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; размер максимального поддерева уменьшится как минимум на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Значит не более чем за &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; шагов наши действия прекратятся и мы окажемся в центроиде дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
Итак, мы конструктивно доказали существование центроида и привели линейный относительно размера дерева алгоритм его нахождения. &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Реализация ===&lt;br /&gt;
&lt;br /&gt;
Поиск центроида в дереве:&lt;br /&gt;
&lt;br /&gt;
  '''int''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int''' sz[n])&lt;br /&gt;
     max_subtree = -1&lt;br /&gt;
     '''for''' u : children(v)&lt;br /&gt;
          '''if''' sz[u] &amp;gt; sz[v] / 2 and sz[u] &amp;gt; sz[max_subtree] &amp;lt;font color=green&amp;gt;// считаем sz[-1] = 0 &amp;lt;/font &amp;gt;&lt;br /&gt;
               max_subtree = u&lt;br /&gt;
     '''if''' max_subtree == -1&lt;br /&gt;
          return v&lt;br /&gt;
     '''else''' &lt;br /&gt;
          '''return''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz)&lt;br /&gt;
&lt;br /&gt;
Шаблон решения произвольной задачи на статическую центроидную декомпозицию:&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int''' sz[n])&lt;br /&gt;
     c = &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz) &amp;lt;font color=green&amp;gt;// находим c {{---}} центроид поддерева вершины v &amp;lt;/font &amp;gt;&lt;br /&gt;
     ch = children[c]&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{deleteEdges}&amp;lt;/tex&amp;gt;(c, children) &amp;lt;font color=green&amp;gt;// удаляем все ребра между вершиной с и детьми, чтобы мы не смогли из детей попасть в с. &lt;br /&gt;
         Это полезно делать, если решение подзадачи для поддерева предполагает проход dfs &amp;lt;/font &amp;gt;&lt;br /&gt;
     '''for''' c2 : ch[c]&lt;br /&gt;
          &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;(children, c2, sz)&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{mergeSolution}&amp;lt;/tex&amp;gt;(children, c2) &amp;lt;font color=green&amp;gt;// решаем для текущего поддерева &amp;lt;/font &amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Динамическая центроидная декомпозиция (дерево центроидной декомпозиции) ==&lt;br /&gt;
Теперь вернемся ко второй задаче введения. Для ее решения мы пользовались динамичекой версией divide &amp;amp; conquer {{---}} деревом отрезков. В предыдущем пункте мы определили статический аналог divide &amp;amp; conquer для дерева. Теперь обобщим этот метод для динамических задач.&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Деревом центроидов (или центроидной декомпозицией)''' дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; называют дерево &amp;lt;math&amp;gt;T(t)&amp;lt;/math&amp;gt;, построенное на вершинах дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; следующим образом :&lt;br /&gt;
* Пусть вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} центроид дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Объявим его корнем нового дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пусть при удалении вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; из дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; оно распалось на поддеревья &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, тогда детьми вершины c объявим &amp;lt;tex&amp;gt;T(t_1), T(t_t),\dots, T(t_k)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
=== Свойства центроидной декомпозиции ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
'''Свойства центроидной декомпозиции''' :&lt;br /&gt;
* Глубина дерева центроидов не превосходит &amp;lt;tex&amp;gt;log(n)&amp;lt;/tex&amp;gt;. &lt;br /&gt;
* Каждая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; является центроидом одного из поддеревьев дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;&lt;br /&gt;
* Каждая вершина дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; поддеревьям дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (еще говорят, что вершина принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; центроидам дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, или что эти центроиды содержат вершину)&lt;br /&gt;
* Для любых вершин &amp;lt;tex&amp;gt;u, v \in T (u \neq v)&amp;lt;/tex&amp;gt; верно ровно одно из следующих трех утверждений: &lt;br /&gt;
a) &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
b) &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
c) &amp;lt;tex&amp;gt;T(u) \cap T(v) = \emptyset &amp;lt;/tex&amp;gt;&lt;br /&gt;
* Простой путь между любой парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; содержит центроид &amp;lt;tex&amp;gt;c \in T(t)&amp;lt;/tex&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
* Действительно, т.к. размер поддерева &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; не превосходит &amp;lt;tex&amp;gt;\frac{|subtree(c)|}{2}&amp;lt;/tex&amp;gt;, то при спуске в каждую следующую вершину на пути к любому листу в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; размер поддерева вершины, в которой мы сейчас находимся, уменьшается как минимум на &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;. Значит длина всего пути до листа не превосходит &amp;lt;math&amp;gt;log(n)&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
* Второе свойство очевидно из построения дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, т.к. если вершина принадлежит дереву центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то она является центроидом, а из построения &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы знаем, что каждая вершина исходного дерева принадлежит и дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Третье свойство {{---}} прямое следствие первых двух, т.к. вершина принадлежит любому центроиду &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; т.и т.т., когда c {{---}} отец вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов. Т.к. вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; точно принадлежит дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (свойство 2), то она лежит на каком-то пути в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, причем все ее родители (центроиды) ее содержат. А по свойству 1 длина любого вертикального (и даже простого) пути есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
* Четвертое свойство очевидно из того, что &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; {{---}} дерево. Т.к. &amp;lt;math&amp;gt;T(u)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;T(v)&amp;lt;/math&amp;gt; {{---}} поддеревья различных вершин дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то либо они не пересекаются, либо &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;, либо &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
* Для доказательства последнего свойства выберем в качестве вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;lca(u, v)&amp;lt;/math&amp;gt; в дереве центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Покажем, что так выбранная вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удовлетворяет заявленным свойствам. То, что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt; {{---}} очевидно по определению &amp;lt;math&amp;gt;lca&amp;lt;/math&amp;gt;, т.к. каждый предок любой вершины в дереве центроидов содержит эту вершину. Теперь докажем, что &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежит на пути между парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt;. Т.к. &amp;lt;math&amp;gt;c = lca(u, v)&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то из &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; нет ребра в такого сына, который содержит одновременно &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в своем поддереве (в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;), значит после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; разделится на несколько поддеревьев, таких что вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; окажутся в разных компонентах связности. А значит найдется такое ребро &amp;lt;math&amp;gt;(c, x)&amp;lt;/math&amp;gt;, которое принадлежало пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, но после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удалилось. Это доказывает то, что вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежала на пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
С помощью описанных свойств дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы можем решить задачу 2 для дерева :&lt;br /&gt;
&lt;br /&gt;
=== Пример решения задачи с помощью центроидной декомпозиции === &lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин. В каждый момент времени любая вершина дерева может быть либо помечена, либо нет. Изначально помечена только вершина номер &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Дан список из &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; запросов: &lt;br /&gt;
* Вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; пометили.&lt;br /&gt;
* С вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; сняли пометку.&lt;br /&gt;
* Найти номер ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
'''Решение :'''&lt;br /&gt;
&lt;br /&gt;
Построим центроидную декомпозицию &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Изначально посчитаем для каждого центроида, содержащего вершину &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt; посчитаем расстояние до вершины &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Для этого воспользуемся [[Метод_двоичного_подъема|методом двоичных подъемов для поиска lca пары вершин в дереве]], а также тем фактом, что если глубина &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве определена как расстояние от корня до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то длина пути между парой вершин &amp;lt;math&amp;gt;(u, v)&amp;lt;/math&amp;gt; есть &amp;lt;tex&amp;gt;len(u, v) = h(u) + h(v) - 2 \cdot h(lca(u, v))&amp;lt;/tex&amp;gt;. Если изначально предпосчитать проходом [[Обход_в_глубину,_цвета_вершин| dfs]] величины &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, то ответ на запрос &amp;lt;math&amp;gt;len(u, v)&amp;lt;/math&amp;gt; можно делать за время &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с &amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt;доп. памятью. Также можно воспользоваться техникой [[Сведение_задачи_LCA_к_задаче_RMQ|сведения задачи LCA к RMQ]] и решить с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; времени на запрос. Теперь научимся отвечать на запросы. Из последнего свойства центроидной декомпозиции видно, что если &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} искомая ближайшая помеченная вершина к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то путь между ними содержит центроид &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;, причем &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок одновременно вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве&amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Поэтому заведем [[Красно-черное_дерево|двоичное дерево поиска]] для каждого центроида &amp;lt;math&amp;gt;c \in T&amp;lt;/math&amp;gt;. В этой структуре для каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; будем хранить пары &amp;lt;math&amp;gt;(len(c, u), u)&amp;lt;/math&amp;gt; для всех помеченных вершин &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в поддереве центроидов &amp;lt;math&amp;gt;T(c)&amp;lt;/math&amp;gt;. Когда приходит запрос пометить вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} добавим в структуру данных для всех предков &amp;lt;math&amp;gt;p_c&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; пары &amp;lt;math&amp;gt;(len(p_c, v), v)&amp;lt;/math&amp;gt;. Мы совершим &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; добавлений, затратив &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; действий на каждое. Запрос снятия пометки с вершины обрабатывается аналогичными удалениями. Запрос поиска ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины {{---}} это запрос поиска вершины u, такой что  величина &amp;lt;math&amp;gt;len(c, u) + len(c, v)&amp;lt;/math&amp;gt; минимальна, где &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов (по пятому свойству, нас интересуют именно такие &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;). Этот запрос занимает так же &amp;lt;tex&amp;gt;O(log^2(n))&amp;lt;/tex&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
Итак, мы научились решать задачу с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и временной сложностью &amp;lt;math&amp;gt;O(log^2(n))&amp;lt;/math&amp;gt; на запрос любого типа с предварительным предпосчетом за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Варианты хранения центроидной декомпозиции ==&lt;br /&gt;
Для хранения дерева центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; есть &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; подхода, имеющих свои преимущества и недостатки :&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева запомним величину &amp;lt;math&amp;gt;p_v&amp;lt;/math&amp;gt; {{---}} номер предка вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Этот подход наиболее экономный по памяти (&amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;), но уступает в скорости и функциональности.&lt;br /&gt;
&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева будем хранить весь массив предков &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt; в дереве центроидов.&lt;br /&gt;
&lt;br /&gt;
Этот подход уступает в количестве необходимой дополнительной памяти (&amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt; суммарно), но имеет ряд преимуществ :&lt;br /&gt;
&lt;br /&gt;
#При проходе по массиву предков фиксированной вершины будет выигрыш в скорости работы, т.к. весь массив будет лежать непрерывным блоком данных и следовательно будет закэширован&lt;br /&gt;
# На массиве предков можно строить различные структуры данных (такие как, например, дерево отрезков) для быстрого (в случае с деревом отрезков &amp;lt;math&amp;gt;O(log(log(n)))&amp;lt;/math&amp;gt; на запрос) поиска предка с необходимыми свойствами. Так, например, в описанной выше задаче про помеченные вершины наибольшего общего предка можно искать методом двоичных подъемов за &amp;lt;math&amp;gt;O(log(log(n))&amp;lt;/math&amp;gt; на запрос, т.к. размер массива предков есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; (по свойству 1 центроидной декомпозиции). Используя эту оптимизацию можно получить время &amp;lt;math&amp;gt;O(log(n)) + O(log(log(n)) = O(log(n))&amp;lt;/math&amp;gt; на запрос нахождения ближайшей помеченной вершины. Чтобы добиться улучшенной асимптотики для запросов изменения можно хранить дерево отрезков на каждом из путей &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt;, в каждой вершине которого хранить двоичное дерево поиска и поддерживать отложенные операции. Тогда ответ на эти запросы будет занимать &amp;lt;math&amp;gt;O(log(n) \cdot log(log(n))&amp;lt;/math&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
&lt;br /&gt;
*[[:Дерево_отрезков._Построение|Дерево отрезков]]&lt;br /&gt;
*[[:Heavy-light_декомпозиция|Heavy-light декомпозиция]]&lt;br /&gt;
*[[:Метод_двоичного_подъема| Метод двоичного подъема]]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы и структуры данных]]&lt;/div&gt;</summary>
		<author><name>92.255.113.52</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61630</id>
		<title>Centroid decomposition</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61630"/>
				<updated>2017-06-14T21:39:04Z</updated>
		
		<summary type="html">&lt;p&gt;92.255.113.52: /* Пример решения задачи с помощью центроидной декомпозиции */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Centroid decomposition (рус. центроидная декомпозиция) {{---}} это структура данных, позволяющая отвечать на запросы на дереве. Чаще всего это запросы, связанные с нахождением функции на вершинах, связанных неравенством на расстояние между ними в дереве. Также иногда применяется для запросов на путях в дереве.&lt;br /&gt;
&lt;br /&gt;
== Введение ==&lt;br /&gt;
Рассмотрим &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; задачи на обычном массиве (в дальнейшем мы будем их обобщать на случай дерева):&lt;br /&gt;
&lt;br /&gt;
Задача &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть массив &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; положительных целых чисел из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов. Также дано число &amp;lt;tex&amp;gt;W \geqslant 0&amp;lt;/tex&amp;gt; и число &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; индексов массива, таких что &amp;lt;tex&amp;gt;|j - i| \leqslant l &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\sum_{i=0}^{n - 1} a_i \leqslant W&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
Задача &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть прямая дорога, на которой расположены &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; городов. В некоторых городах есть госпитали, которые могут принимать больных. Поступают запросы вида :&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;, в котором находится больной и требуется найти такой город &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;|u - v|&amp;lt;/tex&amp;gt; минимально возможное.&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что больше он не будет принимать больных&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что теперь он может принимать больных&lt;br /&gt;
}}&lt;br /&gt;
Для начала решим обе задачи. &lt;br /&gt;
Первая задача решается методом [[Сортировка_слиянием|divide &amp;amp; conquer (рус. разделяй и властвуй)]] {{---}} давайте разделим массив &amp;lt;tex&amp;gt;a[0 \dots n-1]&amp;lt;/tex&amp;gt; на 2 массива &amp;lt;tex&amp;gt;a[0\dots\frac{n}{2} - 1]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;a[\frac{n}{2} \dots n-1]&amp;lt;/tex&amp;gt; и рекурсивно решим задачу для каждого из них. Осталось научиться находить количество искомых пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt;, таких что &amp;lt;tex&amp;gt;i &amp;lt; \frac{n}{2}, j \geqslant \frac{n}{2}&amp;lt;/tex&amp;gt;. Для этого воспользуемся другой известной техникой {{---}} методом двух указателей. Посчитаем массив префиксных сумм для правой половины &amp;lt;tex&amp;gt;pref[i] = \sum_{j=\frac{n}{2}}^{i} a_j&amp;lt;/tex&amp;gt; и суффиксных (&amp;lt;tex&amp;gt;suf[i] = \sum_{j=i}^{\frac{n}{2} + 1} a_j&amp;lt;/tex&amp;gt;) {{---}} для левой. Заведем два указателя (&amp;lt;tex&amp;gt;p_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;p_2&amp;lt;/tex&amp;gt;). Изначально установим &amp;lt;tex&amp;gt;p_1 = \frac{n}{2} - l + 1, p_2 = \frac{n}{2}&amp;lt;/tex&amp;gt;. Пока &amp;lt;tex&amp;gt;p_2 - 1&amp;gt; \frac{n}{2}&amp;lt;/tex&amp;gt; и&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;pref[p_2] + suf[p_1] &amp;gt; W &amp;lt;/tex&amp;gt; будем уменьшать &amp;lt;math&amp;gt;p_2&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Если после этого &amp;lt;math&amp;gt;pref[p_2] + suf[p_1] \leqslant W&amp;lt;/math&amp;gt;, то к ответу прибавим &amp;lt;math&amp;gt;(p_2 - \frac{n}{2} + 1) * (\frac{n}{2} - p_1)&amp;lt;/math&amp;gt;, посго, увеличим &amp;lt;math&amp;gt;p_1&amp;lt;/math&amp;gt; на &amp;lt;math/math&amp;gt;. Так будем делать, пока &amp;lt;math&amp;gt;p_1 &amp;lt; \frac{n}{2}&amp;lt;/math&amp;gt;. В конце сложим текущий ответ и ответы для половин массива {{---}} получим ответ на задачу. Асимптотика такого алгоритма: &amp;lt;tex&amp;gt;T(n) = 2 * T(n / 2) + O(n) = O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вторая задача имеет запросы на изменение и поэтому надо применить динамическую версию divide &amp;amp; conquer {{---}} [[Дерево_отрезков._Построение|дерево отрезков]]. Построим дерево отрезков,&lt;br /&gt;
поддерживающее 2 вида запросов: присвоение в точке и минимум на отрезке. Изначально сделаем так, чтобы дереву отрезков соответствовал массив &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;b_i = 0&amp;lt;/tex&amp;gt;, если в i-м городе принимает госпиталь и &amp;lt;tex&amp;gt;b_i = 1&amp;lt;/tex&amp;gt; иначе. Когда в каком-то городе открывается/закрывается госпиталь {{---}} делаем запрос на изменение в дереве отрезков. Когда требуется узнать ближайщий госпиталь к &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-му городу, можно воспользоваться одной из следующих идей: &lt;br /&gt;
а) (&amp;lt;tex&amp;gt;O(n \cdot log^2(n)&amp;lt;/tex&amp;gt;) Бинарным поиском ищем ближайший слева и ближайший справа к i-му городу госпиталь (такой город &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, что &amp;lt;tex&amp;gt;\min\limits_{k=i..j}b_k= 1&amp;lt;/tex&amp;gt;). Для этого внутри бинарного поиска каждый раз делаем запрос на поиск минимума в дереве отрезков.  &lt;br /&gt;
б) (&amp;lt;tex&amp;gt;O(n \cdot log(n)&amp;lt;/tex&amp;gt;) Будем одним спуском/подъемом по дереву определять, куда нам нужно идти (в левое или правое поддерево), тем самым делая одновоременно и бинарный поиск, и спуск/подъем по дереву.&lt;br /&gt;
&lt;br /&gt;
== Статическая центроидная декомпозиция ==&lt;br /&gt;
Перейдем к обобщению поставленных задач на случай дерева. Начнем, как и полагается, с первой:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть взвешенное дерево &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин, в каждой вершине которого написаны положительные целые числа. Также по-прежнему даны числа &amp;lt;tex&amp;gt;W &amp;gt;= 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; вершин дерева, таких что расстояние между ними не превосходит &amp;lt;math&amp;gt;l&amp;lt;/math&amp;gt; по числу ребер и не превосходит &amp;lt;math&amp;gt;W&amp;lt;/math&amp;gt; по сумме весов.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Для решения новой задачи применим ту же идею, что и была до этого {{---}} разделяй и властвуй. Для этого нам потребуется следующий объект :&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|id=191213&lt;br /&gt;
|definition =&lt;br /&gt;
'''Центроидом дерева''' (англ. ''centroid'') называется такая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, после удаления которой дерево разбивается на несколько (&amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;) поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, таких что для каждого &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;: &amp;lt;tex&amp;gt;|t_i| \leqslant \frac{n}{2}&amp;lt;/tex&amp;gt;, т.е. размер каждого поддерева не превосходит половины размера исходного дерева.&lt;br /&gt;
}}&lt;br /&gt;
Итак, в случае дерева идея разделяй-и-властвуй из предыдущего пункта будет формулироваться так: найдем центроид. Предположим, что мы сумели найти центроид за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; {{---}} размер дерева. Тогда, как и в упрощенной версии задачи {{---}} рекурсивно найдем ответ для всех поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, после чего попытаемся найти недостающие пары вершин, находящиехя в разных поддеревьях и удовлетворяющих вопросу задачи. Для этого будем отвечать на следующие запросы: пусть мы сейчас считаем все пары, где первая из вершин находится в поддереве &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и мы в некоторой структуре данных &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; храним все вершины остальных деревьев (каждую вершину задаем парой &amp;lt;math&amp;gt;(depth(v), length(v))&amp;lt;/math&amp;gt; {{---}} глубина вершины и длина пути до нее из корня поддерева), расстояние до которых от корня их поддерева не превышает &amp;lt;math&amp;gt;min(l, n)&amp;lt;/math&amp;gt;. Тогда просто пройдемся по всем вершинам &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; поддерева &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и прибавим к ответу число вершин в структуре &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt;, таких, что &amp;lt;tex&amp;gt;depth(u) \leqslant l - depth(v)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;length(u) \leqslant l - length(v)&amp;lt;/tex&amp;gt;. Это двумерные запросы, на которые можно отвечать за &amp;lt;math&amp;gt;O(log^2(n)&amp;lt;/math&amp;gt; с помощью [[Многомерное_дерево_отрезков|2d-дерева отрезков]], либо за &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с помощью [[Перечисление_точек_в_произвольном_прямоугольнике_за_n_*_log_%5E(d_-_1)_n_(range_tree)|техники поиска точек в d-мерном пространстве]]. Также читателю предлагается придумать и более эффективные и простые способы решить эту подзадачу.&lt;br /&gt;
&lt;br /&gt;
Оценим итоговую асимптотику: &amp;lt;tex&amp;gt;T(n) = k * T(n / k) + O(n \cdot log(n))&amp;lt;/tex&amp;gt;. Решая это рекурентное соотношение, получим &amp;lt;math&amp;gt;O(n \cdot log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь, как и было обещено, докажем лемму о существовании центроида и опишем алгоритм его эффективного поиска.&lt;br /&gt;
&lt;br /&gt;
=== Лемма о существовании центроида и алгоритм его нахождения. ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
В любом дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; существует центроид.&lt;br /&gt;
|proof=&lt;br /&gt;
Рассмотрим корень дерева &amp;lt;math&amp;gt;(r)&amp;lt;/math&amp;gt;. Положим изначально &amp;lt;math&amp;gt;v = r&amp;lt;/math&amp;gt;. Изначально &amp;lt;math&amp;gt;|subtree(v)| = n&amp;lt;/math&amp;gt;. Среди всех детей &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; выберем вершину &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; с максимальным размером поддерева. Если &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид, то положим &amp;lt;math&amp;gt;v = u&amp;lt;/math&amp;gt; и продолжим выбор нового u, иначе {{---}} остановимся. Докажем, что мы в какой-то момент остановимся. Пусть в призвольный момент времени &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид и размер её наддерева меньше &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, значит максимальное поддерево имеет размер больше чем &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, т.е. &amp;lt;math&amp;gt;|subtree(u)| &amp;gt; \frac{n}{2}&amp;lt;/math&amp;gt;, а значит размер &amp;quot;наддерева&amp;quot; вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; равен &amp;lt;tex&amp;gt;n - |subtree(u)| &amp;lt; \frac{n}{2}&amp;lt;/tex&amp;gt;. При этом теперь размер любого поддерева, на которое распадется дерево t при удалении вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; не превосходит &amp;lt;math&amp;gt;|subtree(u)| - 1&amp;lt;/math&amp;gt;, т.к. наддерево имеет размер меньше, чем поддерево &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, а любое поддерево вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; имеет хотя бы на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; вершину меньше (сама вершина &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;). По индукции получаем, что в любой момент времени размер наддерева вершины v меньше &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, значит мы будем спускаться только вниз по дереву &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, и при переходе к вершине &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} сыну &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; размер максимального поддерева уменьшится как минимум на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Значит не более чем за &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; шагов наши действия прекратятся и мы окажемся в центроиде дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
Итак, мы конструктивно доказали существование центроида и привели линейный относительно размера дерева алгоритм его нахождения. &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Реализация ===&lt;br /&gt;
&lt;br /&gt;
Поиск центроида в дереве:&lt;br /&gt;
&lt;br /&gt;
  '''int''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int''' sz[n])&lt;br /&gt;
     max_subtree = -1&lt;br /&gt;
     '''for''' u : children(v)&lt;br /&gt;
          '''if''' sz[u] &amp;gt; sz[v] / 2 and sz[u] &amp;gt; sz[max_subtree] &amp;lt;font color=green&amp;gt;// считаем sz[-1] = 0 &amp;lt;/font &amp;gt;&lt;br /&gt;
               max_subtree = u&lt;br /&gt;
     '''if''' max_subtree == -1&lt;br /&gt;
          return v&lt;br /&gt;
     '''else''' &lt;br /&gt;
          '''return''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz)&lt;br /&gt;
&lt;br /&gt;
Шаблон решения произвольной задачи на статическую центроидную декомпозицию:&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int''' sz[n])&lt;br /&gt;
     c = &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz) &amp;lt;font color=green&amp;gt;// находим c {{---}} центроид поддерева вершины v &amp;lt;/font &amp;gt;&lt;br /&gt;
     ch = children[c]&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{deleteEdges}&amp;lt;/tex&amp;gt;(c, children) &amp;lt;font color=green&amp;gt;// удаляем все ребра между вершиной с и детьми, чтобы мы не смогли из детей попасть в с. &lt;br /&gt;
         Это полезно делать, если решение подзадачи для поддерева предполагает проход dfs &amp;lt;/font &amp;gt;&lt;br /&gt;
     '''for''' c2 : ch[c]&lt;br /&gt;
          &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;(children, c2, sz)&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{mergeSolution}&amp;lt;/tex&amp;gt;(children, c2) &amp;lt;font color=green&amp;gt;// решаем для текущего поддерева &amp;lt;/font &amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Динамическая центроидная декомпозиция (дерево центроидной декомпозиции) ==&lt;br /&gt;
Теперь вернемся ко второй задаче введения. Для ее решения мы пользовались динамичекой версией divide &amp;amp; conquer {{---}} деревом отрезков. В предыдущем пункте мы определили статический аналог divide &amp;amp; conquer для дерева. Теперь обобщим этот метод для динамических задач.&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Деревом центроидов (или центроидной декомпозицией)''' дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; называют дерево &amp;lt;math&amp;gt;T(t)&amp;lt;/math&amp;gt;, построенное на вершинах дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; следующим образом :&lt;br /&gt;
* Пусть вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} центроид дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Объявим его корнем нового дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пусть при удалении вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; из дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; оно распалось на поддеревья &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, тогда детьми вершины c объявим &amp;lt;tex&amp;gt;T(t_1), T(t_t),\dots, T(t_k)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
=== Свойства центроидной декомпозиции ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
'''Свойства центроидной декомпозиции''' :&lt;br /&gt;
* Глубина дерева центроидов не превосходит &amp;lt;tex&amp;gt;log(n)&amp;lt;/tex&amp;gt;. &lt;br /&gt;
* Каждая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; является центроидом одного из поддеревьев дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;&lt;br /&gt;
* Каждая вершина дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; поддеревьям дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (еще говорят, что вершина принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; центроидам дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, или что эти центроиды содержат вершину)&lt;br /&gt;
* Для любых вершин &amp;lt;tex&amp;gt;u, v \in T (u \neq v)&amp;lt;/tex&amp;gt; верно ровно одно из следующих трех утверждений: &lt;br /&gt;
a) &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
b) &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
c) &amp;lt;tex&amp;gt;T(u) \cap T(v) = \emptyset &amp;lt;/tex&amp;gt;&lt;br /&gt;
* Простой путь между любой парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; содержит центроид &amp;lt;tex&amp;gt;c \in T(t)&amp;lt;/tex&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
* Действительно, т.к. размер поддерева &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; не превосходит &amp;lt;tex&amp;gt;\frac{|subtree(c)|}{2}&amp;lt;/tex&amp;gt;, то при спуске в каждую следующую вершину на пути к любому листу в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; размер поддерева вершины, в которой мы сейчас находимся, уменьшается как минимум на &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;. Значит длина всего пути до листа не превосходит &amp;lt;math&amp;gt;log(n)&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
* Второе свойство очевидно из построения дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, т.к. если вершина принадлежит дереву центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то она является центроидом, а из построения &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы знаем, что каждая вершина исходного дерева принадлежит и дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Третье свойство {{---}} прямое следствие первых двух, т.к. вершина принадлежит любому центроиду &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; т.и т.т., когда c {{---}} отец вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов. Т.к. вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; точно принадлежит дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (свойство 2), то она лежит на каком-то пути в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, причем все ее родители (центроиды) ее содержат. А по свойству 1 длина любого вертикального (и даже простого) пути есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
* Четвертое свойство очевидно из того, что &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; {{---}} дерево. Т.к. &amp;lt;math&amp;gt;T(u)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;T(v)&amp;lt;/math&amp;gt; {{---}} поддеревья различных вершин дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то либо они не пересекаются, либо &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;, либо &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
* Для доказательства последнего свойства выберем в качестве вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;lca(u, v)&amp;lt;/math&amp;gt; в дереве центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Покажем, что так выбранная вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удовлетворяет заявленным свойствам. То, что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt; {{---}} очевидно по определению &amp;lt;math&amp;gt;lca&amp;lt;/math&amp;gt;, т.к. каждый предок любой вершины в дереве центроидов содержит эту вершину. Теперь докажем, что &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежит на пути между парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt;. Т.к. &amp;lt;math&amp;gt;c = lca(u, v)&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то из &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; нет ребра в такого сына, который содержит одновременно &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в своем поддереве (в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;), значит после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; разделится на несколько поддеревьев, таких что вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; окажутся в разных компонентах связности. А значит найдется такое ребро &amp;lt;math&amp;gt;(c, x)&amp;lt;/math&amp;gt;, которое принадлежало пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, но после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удалилось. Это доказывает то, что вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежала на пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
С помощью описанных свойств дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы можем решить задачу 2 для дерева :&lt;br /&gt;
&lt;br /&gt;
=== Пример решения задачи с помощью центроидной декомпозиции === &lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин. В каждый момент времени любая вершина дерева может быть либо помечена, либо нет. Изначально помечена только вершина номер &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Дан список из &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; запросов: &lt;br /&gt;
* Вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; пометили.&lt;br /&gt;
* С вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; сняли пометку.&lt;br /&gt;
* Найти номер ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
'''Решение :'''&lt;br /&gt;
&lt;br /&gt;
Построим центроидную декомпозицию &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Изначально посчитаем для каждого центроида, содержащего вершину &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt; посчитаем расстояние до вершины &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Для этого воспользуемся [[Метод_двоичного_подъема|методом двоичных подъемов для поиска lca пары вершин в дереве]], а также тем фактом, что если глубина &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве определена как расстояние от корня до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то длина пути между парой вершин &amp;lt;math&amp;gt;(u, v)&amp;lt;/math&amp;gt; есть &amp;lt;tex&amp;gt;len(u, v) = h(u) + h(v) - 2 \cdot h(lca(u, v))&amp;lt;/tex&amp;gt;. Если изначально предпосчитать проходом [[Обход_в_глубину,_цвета_вершин| dfs]] величины &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, то ответ на запрос &amp;lt;math&amp;gt;len(u, v)&amp;lt;/math&amp;gt; можно делать за время &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с &amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt;доп. памятью. Также можно воспользоваться техникой [[Сведение_задачи_LCA_к_задаче_RMQ|сведения задачи LCA к RMQ]] и решить с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; времени на запрос. Теперь научимся отвечать на запросы. Из последнего свойства центроидной декомпозиции видно, что если &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} искомая ближайшая помеченная вершина к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то путь между ними содержит центроид &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;, причем &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок одновременно вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве&amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Поэтому заведем [[Красно-черное_дерево|двоичное дерево поиска]] для каждого центроида &amp;lt;math&amp;gt;c \in T&amp;lt;/math&amp;gt;. В этой структуре для каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; будем хранить пары &amp;lt;math&amp;gt;(len(c, u), u)&amp;lt;/math&amp;gt; для всех помеченных вершин &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в поддереве центроидов &amp;lt;math&amp;gt;T(c)&amp;lt;/math&amp;gt;. Когда приходит запрос пометить вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} добавим в структуру данных для всех предков &amp;lt;math&amp;gt;p_c&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; пары &amp;lt;math&amp;gt;(len(p_c, v), v)&amp;lt;/math&amp;gt;. Мы совершим &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; добавлений, затратив &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; действий на каждое. Запрос снятия пометки с вершины обрабатывается аналогичными удалениями. Запрос поиска ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины {{---}} это запрос поиска вершины u, такой что  величина &amp;lt;math&amp;gt;len(c, u) + len(c, v)&amp;lt;/math&amp;gt; минимальна, где &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов (по пятому свойству, нас интересуют именно такие &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;). Этот запрос занимает так же &amp;lt;tex&amp;gt;O(log^2(n))&amp;lt;/tex&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
Итак, мы научились решать задачу с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и временной сложностью &amp;lt;math&amp;gt;O(log^2(n))&amp;lt;/math&amp;gt; на запрос любого типа с предварительным предпосчетом за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Варианты хранения центроидной декомпозиции ==&lt;br /&gt;
Для хранения дерева центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; есть &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; подхода, имеющих свои преимущества и недостатки :&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева запомним величину &amp;lt;math&amp;gt;p_v&amp;lt;/math&amp;gt; {{---}} номер предка вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Этот подход наиболее экономный по памяти (&amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;), но уступает в скорости и функциональности.&lt;br /&gt;
&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева будем хранить весь массив предков &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt; в дереве центроидов.&lt;br /&gt;
&lt;br /&gt;
Этот подход уступает в количестве необходимой дополнительной памяти (&amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt; суммарно), но имеет ряд преимуществ :&lt;br /&gt;
&lt;br /&gt;
1) При проходе по массиву предков фиксированной вершины будет выигрыш в скорости работы, т.к. весь массив будет лежать непрерывным блоком данных и следовательно будет закэширован&lt;br /&gt;
&lt;br /&gt;
2) На массиве предков можно строить различные структуры данных (такие как, например, дерево отрезков) для быстрого (в случае с деревом отрезков &amp;lt;math&amp;gt;O(log(log(n)))&amp;lt;/math&amp;gt; на запрос) поиска предка с необходимыми свойствами. Так, например, в описанной выше задаче про помеченные вершины наибольшего общего предка можно искать методом двоичных подъемов за &amp;lt;math&amp;gt;O(log(log(n))&amp;lt;/math&amp;gt; на запрос, т.к. размер массива предков есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; (по свойству 1 центроидной декомпозиции). Используя эту оптимизацию можно получить время &amp;lt;math&amp;gt;O(log(n)) + O(log(log(n)) = O(log(n))&amp;lt;/math&amp;gt; на запрос нахождения ближайшей помеченной вершины. Чтобы добиться улучшенной асимптотики для запросов изменения можно хранить дерево отрезков на каждом из путей &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt;, в каждой вершине которого хранить двоичное дерево поиска и поддерживать отложенные операции. Тогда ответ на эти запросы будет занимать &amp;lt;math&amp;gt;O(log(n) \cdot log(log(n))&amp;lt;/math&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
&lt;br /&gt;
*[[:Дерево_отрезков._Построение|Дерево отрезков]]&lt;br /&gt;
*[[:Heavy-light_декомпозиция|Heavy-light декомпозиция]]&lt;br /&gt;
*[[:Метод_двоичного_подъема| Метод двоичного подъема]]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы и структуры данных]]&lt;/div&gt;</summary>
		<author><name>92.255.113.52</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61629</id>
		<title>Centroid decomposition</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61629"/>
				<updated>2017-06-14T21:38:31Z</updated>
		
		<summary type="html">&lt;p&gt;92.255.113.52: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Centroid decomposition (рус. центроидная декомпозиция) {{---}} это структура данных, позволяющая отвечать на запросы на дереве. Чаще всего это запросы, связанные с нахождением функции на вершинах, связанных неравенством на расстояние между ними в дереве. Также иногда применяется для запросов на путях в дереве.&lt;br /&gt;
&lt;br /&gt;
== Введение ==&lt;br /&gt;
Рассмотрим &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; задачи на обычном массиве (в дальнейшем мы будем их обобщать на случай дерева):&lt;br /&gt;
&lt;br /&gt;
Задача &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть массив &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; положительных целых чисел из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов. Также дано число &amp;lt;tex&amp;gt;W \geqslant 0&amp;lt;/tex&amp;gt; и число &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; индексов массива, таких что &amp;lt;tex&amp;gt;|j - i| \leqslant l &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\sum_{i=0}^{n - 1} a_i \leqslant W&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
Задача &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть прямая дорога, на которой расположены &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; городов. В некоторых городах есть госпитали, которые могут принимать больных. Поступают запросы вида :&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;, в котором находится больной и требуется найти такой город &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;|u - v|&amp;lt;/tex&amp;gt; минимально возможное.&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что больше он не будет принимать больных&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что теперь он может принимать больных&lt;br /&gt;
}}&lt;br /&gt;
Для начала решим обе задачи. &lt;br /&gt;
Первая задача решается методом [[Сортировка_слиянием|divide &amp;amp; conquer (рус. разделяй и властвуй)]] {{---}} давайте разделим массив &amp;lt;tex&amp;gt;a[0 \dots n-1]&amp;lt;/tex&amp;gt; на 2 массива &amp;lt;tex&amp;gt;a[0\dots\frac{n}{2} - 1]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;a[\frac{n}{2} \dots n-1]&amp;lt;/tex&amp;gt; и рекурсивно решим задачу для каждого из них. Осталось научиться находить количество искомых пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt;, таких что &amp;lt;tex&amp;gt;i &amp;lt; \frac{n}{2}, j \geqslant \frac{n}{2}&amp;lt;/tex&amp;gt;. Для этого воспользуемся другой известной техникой {{---}} методом двух указателей. Посчитаем массив префиксных сумм для правой половины &amp;lt;tex&amp;gt;pref[i] = \sum_{j=\frac{n}{2}}^{i} a_j&amp;lt;/tex&amp;gt; и суффиксных (&amp;lt;tex&amp;gt;suf[i] = \sum_{j=i}^{\frac{n}{2} + 1} a_j&amp;lt;/tex&amp;gt;) {{---}} для левой. Заведем два указателя (&amp;lt;tex&amp;gt;p_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;p_2&amp;lt;/tex&amp;gt;). Изначально установим &amp;lt;tex&amp;gt;p_1 = \frac{n}{2} - l + 1, p_2 = \frac{n}{2}&amp;lt;/tex&amp;gt;. Пока &amp;lt;tex&amp;gt;p_2 - 1&amp;gt; \frac{n}{2}&amp;lt;/tex&amp;gt; и&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;pref[p_2] + suf[p_1] &amp;gt; W &amp;lt;/tex&amp;gt; будем уменьшать &amp;lt;math&amp;gt;p_2&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Если после этого &amp;lt;math&amp;gt;pref[p_2] + suf[p_1] \leqslant W&amp;lt;/math&amp;gt;, то к ответу прибавим &amp;lt;math&amp;gt;(p_2 - \frac{n}{2} + 1) * (\frac{n}{2} - p_1)&amp;lt;/math&amp;gt;, посго, увеличим &amp;lt;math&amp;gt;p_1&amp;lt;/math&amp;gt; на &amp;lt;math/math&amp;gt;. Так будем делать, пока &amp;lt;math&amp;gt;p_1 &amp;lt; \frac{n}{2}&amp;lt;/math&amp;gt;. В конце сложим текущий ответ и ответы для половин массива {{---}} получим ответ на задачу. Асимптотика такого алгоритма: &amp;lt;tex&amp;gt;T(n) = 2 * T(n / 2) + O(n) = O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вторая задача имеет запросы на изменение и поэтому надо применить динамическую версию divide &amp;amp; conquer {{---}} [[Дерево_отрезков._Построение|дерево отрезков]]. Построим дерево отрезков,&lt;br /&gt;
поддерживающее 2 вида запросов: присвоение в точке и минимум на отрезке. Изначально сделаем так, чтобы дереву отрезков соответствовал массив &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;b_i = 0&amp;lt;/tex&amp;gt;, если в i-м городе принимает госпиталь и &amp;lt;tex&amp;gt;b_i = 1&amp;lt;/tex&amp;gt; иначе. Когда в каком-то городе открывается/закрывается госпиталь {{---}} делаем запрос на изменение в дереве отрезков. Когда требуется узнать ближайщий госпиталь к &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-му городу, можно воспользоваться одной из следующих идей: &lt;br /&gt;
а) (&amp;lt;tex&amp;gt;O(n \cdot log^2(n)&amp;lt;/tex&amp;gt;) Бинарным поиском ищем ближайший слева и ближайший справа к i-му городу госпиталь (такой город &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, что &amp;lt;tex&amp;gt;\min\limits_{k=i..j}b_k= 1&amp;lt;/tex&amp;gt;). Для этого внутри бинарного поиска каждый раз делаем запрос на поиск минимума в дереве отрезков.  &lt;br /&gt;
б) (&amp;lt;tex&amp;gt;O(n \cdot log(n)&amp;lt;/tex&amp;gt;) Будем одним спуском/подъемом по дереву определять, куда нам нужно идти (в левое или правое поддерево), тем самым делая одновоременно и бинарный поиск, и спуск/подъем по дереву.&lt;br /&gt;
&lt;br /&gt;
== Статическая центроидная декомпозиция ==&lt;br /&gt;
Перейдем к обобщению поставленных задач на случай дерева. Начнем, как и полагается, с первой:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть взвешенное дерево &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин, в каждой вершине которого написаны положительные целые числа. Также по-прежнему даны числа &amp;lt;tex&amp;gt;W &amp;gt;= 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; вершин дерева, таких что расстояние между ними не превосходит &amp;lt;math&amp;gt;l&amp;lt;/math&amp;gt; по числу ребер и не превосходит &amp;lt;math&amp;gt;W&amp;lt;/math&amp;gt; по сумме весов.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Для решения новой задачи применим ту же идею, что и была до этого {{---}} разделяй и властвуй. Для этого нам потребуется следующий объект :&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|id=191213&lt;br /&gt;
|definition =&lt;br /&gt;
'''Центроидом дерева''' (англ. ''centroid'') называется такая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, после удаления которой дерево разбивается на несколько (&amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;) поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, таких что для каждого &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;: &amp;lt;tex&amp;gt;|t_i| \leqslant \frac{n}{2}&amp;lt;/tex&amp;gt;, т.е. размер каждого поддерева не превосходит половины размера исходного дерева.&lt;br /&gt;
}}&lt;br /&gt;
Итак, в случае дерева идея разделяй-и-властвуй из предыдущего пункта будет формулироваться так: найдем центроид. Предположим, что мы сумели найти центроид за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; {{---}} размер дерева. Тогда, как и в упрощенной версии задачи {{---}} рекурсивно найдем ответ для всех поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, после чего попытаемся найти недостающие пары вершин, находящиехя в разных поддеревьях и удовлетворяющих вопросу задачи. Для этого будем отвечать на следующие запросы: пусть мы сейчас считаем все пары, где первая из вершин находится в поддереве &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и мы в некоторой структуре данных &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; храним все вершины остальных деревьев (каждую вершину задаем парой &amp;lt;math&amp;gt;(depth(v), length(v))&amp;lt;/math&amp;gt; {{---}} глубина вершины и длина пути до нее из корня поддерева), расстояние до которых от корня их поддерева не превышает &amp;lt;math&amp;gt;min(l, n)&amp;lt;/math&amp;gt;. Тогда просто пройдемся по всем вершинам &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; поддерева &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и прибавим к ответу число вершин в структуре &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt;, таких, что &amp;lt;tex&amp;gt;depth(u) \leqslant l - depth(v)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;length(u) \leqslant l - length(v)&amp;lt;/tex&amp;gt;. Это двумерные запросы, на которые можно отвечать за &amp;lt;math&amp;gt;O(log^2(n)&amp;lt;/math&amp;gt; с помощью [[Многомерное_дерево_отрезков|2d-дерева отрезков]], либо за &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с помощью [[Перечисление_точек_в_произвольном_прямоугольнике_за_n_*_log_%5E(d_-_1)_n_(range_tree)|техники поиска точек в d-мерном пространстве]]. Также читателю предлагается придумать и более эффективные и простые способы решить эту подзадачу.&lt;br /&gt;
&lt;br /&gt;
Оценим итоговую асимптотику: &amp;lt;tex&amp;gt;T(n) = k * T(n / k) + O(n \cdot log(n))&amp;lt;/tex&amp;gt;. Решая это рекурентное соотношение, получим &amp;lt;math&amp;gt;O(n \cdot log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь, как и было обещено, докажем лемму о существовании центроида и опишем алгоритм его эффективного поиска.&lt;br /&gt;
&lt;br /&gt;
=== Лемма о существовании центроида и алгоритм его нахождения. ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
В любом дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; существует центроид.&lt;br /&gt;
|proof=&lt;br /&gt;
Рассмотрим корень дерева &amp;lt;math&amp;gt;(r)&amp;lt;/math&amp;gt;. Положим изначально &amp;lt;math&amp;gt;v = r&amp;lt;/math&amp;gt;. Изначально &amp;lt;math&amp;gt;|subtree(v)| = n&amp;lt;/math&amp;gt;. Среди всех детей &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; выберем вершину &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; с максимальным размером поддерева. Если &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид, то положим &amp;lt;math&amp;gt;v = u&amp;lt;/math&amp;gt; и продолжим выбор нового u, иначе {{---}} остановимся. Докажем, что мы в какой-то момент остановимся. Пусть в призвольный момент времени &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид и размер её наддерева меньше &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, значит максимальное поддерево имеет размер больше чем &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, т.е. &amp;lt;math&amp;gt;|subtree(u)| &amp;gt; \frac{n}{2}&amp;lt;/math&amp;gt;, а значит размер &amp;quot;наддерева&amp;quot; вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; равен &amp;lt;tex&amp;gt;n - |subtree(u)| &amp;lt; \frac{n}{2}&amp;lt;/tex&amp;gt;. При этом теперь размер любого поддерева, на которое распадется дерево t при удалении вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; не превосходит &amp;lt;math&amp;gt;|subtree(u)| - 1&amp;lt;/math&amp;gt;, т.к. наддерево имеет размер меньше, чем поддерево &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, а любое поддерево вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; имеет хотя бы на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; вершину меньше (сама вершина &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;). По индукции получаем, что в любой момент времени размер наддерева вершины v меньше &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, значит мы будем спускаться только вниз по дереву &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, и при переходе к вершине &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} сыну &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; размер максимального поддерева уменьшится как минимум на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Значит не более чем за &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; шагов наши действия прекратятся и мы окажемся в центроиде дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
Итак, мы конструктивно доказали существование центроида и привели линейный относительно размера дерева алгоритм его нахождения. &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Реализация ===&lt;br /&gt;
&lt;br /&gt;
Поиск центроида в дереве:&lt;br /&gt;
&lt;br /&gt;
  '''int''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int''' sz[n])&lt;br /&gt;
     max_subtree = -1&lt;br /&gt;
     '''for''' u : children(v)&lt;br /&gt;
          '''if''' sz[u] &amp;gt; sz[v] / 2 and sz[u] &amp;gt; sz[max_subtree] &amp;lt;font color=green&amp;gt;// считаем sz[-1] = 0 &amp;lt;/font &amp;gt;&lt;br /&gt;
               max_subtree = u&lt;br /&gt;
     '''if''' max_subtree == -1&lt;br /&gt;
          return v&lt;br /&gt;
     '''else''' &lt;br /&gt;
          '''return''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz)&lt;br /&gt;
&lt;br /&gt;
Шаблон решения произвольной задачи на статическую центроидную декомпозицию:&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int''' sz[n])&lt;br /&gt;
     c = &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz) &amp;lt;font color=green&amp;gt;// находим c {{---}} центроид поддерева вершины v &amp;lt;/font &amp;gt;&lt;br /&gt;
     ch = children[c]&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{deleteEdges}&amp;lt;/tex&amp;gt;(c, children) &amp;lt;font color=green&amp;gt;// удаляем все ребра между вершиной с и детьми, чтобы мы не смогли из детей попасть в с. &lt;br /&gt;
         Это полезно делать, если решение подзадачи для поддерева предполагает проход dfs &amp;lt;/font &amp;gt;&lt;br /&gt;
     '''for''' c2 : ch[c]&lt;br /&gt;
          &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;(children, c2, sz)&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{mergeSolution}&amp;lt;/tex&amp;gt;(children, c2) &amp;lt;font color=green&amp;gt;// решаем для текущего поддерева &amp;lt;/font &amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Динамическая центроидная декомпозиция (дерево центроидной декомпозиции) ==&lt;br /&gt;
Теперь вернемся ко второй задаче введения. Для ее решения мы пользовались динамичекой версией divide &amp;amp; conquer {{---}} деревом отрезков. В предыдущем пункте мы определили статический аналог divide &amp;amp; conquer для дерева. Теперь обобщим этот метод для динамических задач.&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Деревом центроидов (или центроидной декомпозицией)''' дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; называют дерево &amp;lt;math&amp;gt;T(t)&amp;lt;/math&amp;gt;, построенное на вершинах дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; следующим образом :&lt;br /&gt;
* Пусть вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} центроид дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Объявим его корнем нового дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пусть при удалении вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; из дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; оно распалось на поддеревья &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, тогда детьми вершины c объявим &amp;lt;tex&amp;gt;T(t_1), T(t_t),\dots, T(t_k)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
=== Свойства центроидной декомпозиции ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
'''Свойства центроидной декомпозиции''' :&lt;br /&gt;
* Глубина дерева центроидов не превосходит &amp;lt;tex&amp;gt;log(n)&amp;lt;/tex&amp;gt;. &lt;br /&gt;
* Каждая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; является центроидом одного из поддеревьев дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;&lt;br /&gt;
* Каждая вершина дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; поддеревьям дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (еще говорят, что вершина принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; центроидам дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, или что эти центроиды содержат вершину)&lt;br /&gt;
* Для любых вершин &amp;lt;tex&amp;gt;u, v \in T (u \neq v)&amp;lt;/tex&amp;gt; верно ровно одно из следующих трех утверждений: &lt;br /&gt;
a) &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
b) &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
c) &amp;lt;tex&amp;gt;T(u) \cap T(v) = \emptyset &amp;lt;/tex&amp;gt;&lt;br /&gt;
* Простой путь между любой парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; содержит центроид &amp;lt;tex&amp;gt;c \in T(t)&amp;lt;/tex&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
* Действительно, т.к. размер поддерева &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; не превосходит &amp;lt;tex&amp;gt;\frac{|subtree(c)|}{2}&amp;lt;/tex&amp;gt;, то при спуске в каждую следующую вершину на пути к любому листу в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; размер поддерева вершины, в которой мы сейчас находимся, уменьшается как минимум на &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;. Значит длина всего пути до листа не превосходит &amp;lt;math&amp;gt;log(n)&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
* Второе свойство очевидно из построения дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, т.к. если вершина принадлежит дереву центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то она является центроидом, а из построения &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы знаем, что каждая вершина исходного дерева принадлежит и дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Третье свойство {{---}} прямое следствие первых двух, т.к. вершина принадлежит любому центроиду &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; т.и т.т., когда c {{---}} отец вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов. Т.к. вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; точно принадлежит дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (свойство 2), то она лежит на каком-то пути в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, причем все ее родители (центроиды) ее содержат. А по свойству 1 длина любого вертикального (и даже простого) пути есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
* Четвертое свойство очевидно из того, что &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; {{---}} дерево. Т.к. &amp;lt;math&amp;gt;T(u)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;T(v)&amp;lt;/math&amp;gt; {{---}} поддеревья различных вершин дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то либо они не пересекаются, либо &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;, либо &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
* Для доказательства последнего свойства выберем в качестве вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;lca(u, v)&amp;lt;/math&amp;gt; в дереве центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Покажем, что так выбранная вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удовлетворяет заявленным свойствам. То, что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt; {{---}} очевидно по определению &amp;lt;math&amp;gt;lca&amp;lt;/math&amp;gt;, т.к. каждый предок любой вершины в дереве центроидов содержит эту вершину. Теперь докажем, что &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежит на пути между парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt;. Т.к. &amp;lt;math&amp;gt;c = lca(u, v)&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то из &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; нет ребра в такого сына, который содержит одновременно &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в своем поддереве (в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;), значит после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; разделится на несколько поддеревьев, таких что вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; окажутся в разных компонентах связности. А значит найдется такое ребро &amp;lt;math&amp;gt;(c, x)&amp;lt;/math&amp;gt;, которое принадлежало пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, но после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удалилось. Это доказывает то, что вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежала на пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
С помощью описанных свойств дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы можем решить задачу 2 для дерева :&lt;br /&gt;
&lt;br /&gt;
=== Пример решения задачи с помощью центроидной декомпозиции === &lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин. В каждый момент времени любая вершина дерева может быть либо помечена, либо нет. Изначально помечена только вершина номер &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Дан список из &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; запросов: &lt;br /&gt;
* Вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; пометили.&lt;br /&gt;
* С вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; сняли пометку.&lt;br /&gt;
* Найти номер ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Решение :&lt;br /&gt;
&lt;br /&gt;
Построим центроидную декомпозицию &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Изначально посчитаем для каждого центроида, содержащего вершину &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt; посчитаем расстояние до вершины &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Для этого воспользуемся [[Метод_двоичного_подъема|методом двоичных подъемов для поиска lca пары вершин в дереве]], а также тем фактом, что если глубина &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве определена как расстояние от корня до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то длина пути между парой вершин &amp;lt;math&amp;gt;(u, v)&amp;lt;/math&amp;gt; есть &amp;lt;tex&amp;gt;len(u, v) = h(u) + h(v) - 2 \cdot h(lca(u, v))&amp;lt;/tex&amp;gt;. Если изначально предпосчитать проходом [[Обход_в_глубину,_цвета_вершин| dfs]] величины &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, то ответ на запрос &amp;lt;math&amp;gt;len(u, v)&amp;lt;/math&amp;gt; можно делать за время &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с &amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt;доп. памятью. Также можно воспользоваться техникой [[Сведение_задачи_LCA_к_задаче_RMQ|сведения задачи LCA к RMQ]] и решить с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; времени на запрос. Теперь научимся отвечать на запросы. Из последнего свойства центроидной декомпозиции видно, что если &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} искомая ближайшая помеченная вершина к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то путь между ними содержит центроид &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;, причем &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок одновременно вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве&amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Поэтому заведем [[Красно-черное_дерево|двоичное дерево поиска]] для каждого центроида &amp;lt;math&amp;gt;c \in T&amp;lt;/math&amp;gt;. В этой структуре для каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; будем хранить пары &amp;lt;math&amp;gt;(len(c, u), u)&amp;lt;/math&amp;gt; для всех помеченных вершин &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в поддереве центроидов &amp;lt;math&amp;gt;T(c)&amp;lt;/math&amp;gt;. Когда приходит запрос пометить вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} добавим в структуру данных для всех предков &amp;lt;math&amp;gt;p_c&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; пары &amp;lt;math&amp;gt;(len(p_c, v), v)&amp;lt;/math&amp;gt;. Мы совершим &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; добавлений, затратив &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; действий на каждое. Запрос снятия пометки с вершины обрабатывается аналогичными удалениями. Запрос поиска ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины {{---}} это запрос поиска вершины u, такой что  величина &amp;lt;math&amp;gt;len(c, u) + len(c, v)&amp;lt;/math&amp;gt; минимальна, где &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов (по пятому свойству, нас интересуют именно такие &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;). Этот запрос занимает так же &amp;lt;tex&amp;gt;O(log^2(n))&amp;lt;/tex&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
Итак, мы научились решать задачу с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и временной сложностью &amp;lt;math&amp;gt;O(log^2(n))&amp;lt;/math&amp;gt; на запрос любого типа с предварительным предпосчетом за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Варианты хранения центроидной декомпозиции ==&lt;br /&gt;
Для хранения дерева центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; есть &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; подхода, имеющих свои преимущества и недостатки :&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева запомним величину &amp;lt;math&amp;gt;p_v&amp;lt;/math&amp;gt; {{---}} номер предка вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Этот подход наиболее экономный по памяти (&amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;), но уступает в скорости и функциональности.&lt;br /&gt;
&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева будем хранить весь массив предков &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt; в дереве центроидов.&lt;br /&gt;
&lt;br /&gt;
Этот подход уступает в количестве необходимой дополнительной памяти (&amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt; суммарно), но имеет ряд преимуществ :&lt;br /&gt;
&lt;br /&gt;
1) При проходе по массиву предков фиксированной вершины будет выигрыш в скорости работы, т.к. весь массив будет лежать непрерывным блоком данных и следовательно будет закэширован&lt;br /&gt;
&lt;br /&gt;
2) На массиве предков можно строить различные структуры данных (такие как, например, дерево отрезков) для быстрого (в случае с деревом отрезков &amp;lt;math&amp;gt;O(log(log(n)))&amp;lt;/math&amp;gt; на запрос) поиска предка с необходимыми свойствами. Так, например, в описанной выше задаче про помеченные вершины наибольшего общего предка можно искать методом двоичных подъемов за &amp;lt;math&amp;gt;O(log(log(n))&amp;lt;/math&amp;gt; на запрос, т.к. размер массива предков есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; (по свойству 1 центроидной декомпозиции). Используя эту оптимизацию можно получить время &amp;lt;math&amp;gt;O(log(n)) + O(log(log(n)) = O(log(n))&amp;lt;/math&amp;gt; на запрос нахождения ближайшей помеченной вершины. Чтобы добиться улучшенной асимптотики для запросов изменения можно хранить дерево отрезков на каждом из путей &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt;, в каждой вершине которого хранить двоичное дерево поиска и поддерживать отложенные операции. Тогда ответ на эти запросы будет занимать &amp;lt;math&amp;gt;O(log(n) \cdot log(log(n))&amp;lt;/math&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
&lt;br /&gt;
*[[:Дерево_отрезков._Построение|Дерево отрезков]]&lt;br /&gt;
*[[:Heavy-light_декомпозиция|Heavy-light декомпозиция]]&lt;br /&gt;
*[[:Метод_двоичного_подъема| Метод двоичного подъема]]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы и структуры данных]]&lt;/div&gt;</summary>
		<author><name>92.255.113.52</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61628</id>
		<title>Centroid decomposition</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61628"/>
				<updated>2017-06-14T21:34:30Z</updated>
		
		<summary type="html">&lt;p&gt;92.255.113.52: /* Реализация */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Centroid decomposition (рус. центроидная декомпозиция) {{---}} это структура данных, позволяющая отвечать на запросы на дереве. Чаще всего это запросы, связанные с нахождением функции на вершинах, связанных неравенством на расстояние между ними в дереве. Также иногда применяется для запросов на путях в дереве.&lt;br /&gt;
&lt;br /&gt;
== Введение ==&lt;br /&gt;
Рассмотрим &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; задачи на обычном массиве (в дальнейшем мы будем их обобщать на случай дерева):&lt;br /&gt;
&lt;br /&gt;
Задача &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть массив &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; положительных целых чисел из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов и числа &amp;lt;tex&amp;gt;W \geqslant 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;рl&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; индексов массива, таких что &amp;lt;tex&amp;gt;|j - i| \leqslant l &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\sum_{i=0}^{n - 1} a_i \leqslant W&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
Задача &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть прямая дорога, на которой расположены &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; городов. В некоторых городах есть госпитали, которые могут принимать больных. Поступают запросы вида :&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;, в котором находится больной и требуется найти такой город &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;|u - v|&amp;lt;/tex&amp;gt; минимально возможное.&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что больше он не будет принимать больных&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что теперь он может принимать больных&lt;br /&gt;
}}&lt;br /&gt;
Для начала решим обе задачи. &lt;br /&gt;
Первая задача решается методом [[Сортировка_слиянием|divide &amp;amp; conquer (рус. разделяй и властвуй)]] {{---}} давайте разделим массив &amp;lt;tex&amp;gt;a[0 \dots n-1]&amp;lt;/tex&amp;gt; на 2 массива &amp;lt;tex&amp;gt;a[0\dots\frac{n}{2} - 1]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;a[\frac{n}{2} \dots n-1]&amp;lt;/tex&amp;gt; и рекурсивно решим задачу для каждого из них. Осталось научиться находить количество искомых пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt;, таких что &amp;lt;tex&amp;gt;i &amp;lt; \frac{n}{2}, j \geqslant \frac{n}{2}&amp;lt;/tex&amp;gt;. Для этого воспользуемся другой известной техникой {{---}} методом двух указателей. Посчитаем массив префиксных сумм для правой половины &amp;lt;tex&amp;gt;pref[i] = \sum_{j=\frac{n}{2}}^{i} a_j&amp;lt;/tex&amp;gt; и суффиксных (&amp;lt;tex&amp;gt;suf[i] = \sum_{j=i}^{\frac{n}{2} + 1} a_j&amp;lt;/tex&amp;gt;) {{---}} для левой. Заведем два указателя (&amp;lt;tex&amp;gt;p_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;p_2&amp;lt;/tex&amp;gt;). Изначально установим &amp;lt;tex&amp;gt;p_1 = \frac{n}{2} - l + 1, p_2 = \frac{n}{2}&amp;lt;/tex&amp;gt;. Пока &amp;lt;tex&amp;gt;p_2 - 1&amp;gt; \frac{n}{2}&amp;lt;/tex&amp;gt; и&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;pref[p_2] + suf[p_1] &amp;gt; W &amp;lt;/tex&amp;gt; будем уменьшать &amp;lt;math&amp;gt;p_2&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Если после этого &amp;lt;math&amp;gt;pref[p_2] + suf[p_1] \leqslant W&amp;lt;/math&amp;gt;, то к ответу прибавим &amp;lt;math&amp;gt;(p_2 - \frac{n}{2} + 1) * (\frac{n}{2} - p_1)&amp;lt;/math&amp;gt;, посго, увеличим &amp;lt;math&amp;gt;p_1&amp;lt;/math&amp;gt; на &amp;lt;math/math&amp;gt;. Так будем делать, пока &amp;lt;math&amp;gt;p_1 &amp;lt; \frac{n}{2}&amp;lt;/math&amp;gt;. В конце сложим текущий ответ и ответы для половин массива {{---}} получим ответ на задачу. Асимптотика такого алгоритма: &amp;lt;tex&amp;gt;T(n) = 2 * T(n / 2) + O(n) = O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вторая задача имеет запросы на изменение и поэтому надо применить динамическую версию divide &amp;amp; conquer {{---}} [[Дерево_отрезков._Построение|дерево отрезков]]. Построим дерево отрезков,&lt;br /&gt;
поддерживающее 2 вида запросов: присвоение в точке и минимум на отрезке. Изначально сделаем так, чтобы дереву отрезков соответствовал массив &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;b_i = 0&amp;lt;/tex&amp;gt;, если в i-м городе принимает госпиталь и &amp;lt;tex&amp;gt;b_i = 1&amp;lt;/tex&amp;gt; иначе. Когда в каком-то городе открывается/закрывается госпиталь {{---}} делаем запрос на изменение в дереве отрезков. Когда требуется узнать ближайщий госпиталь к &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-му городу, можно воспользоваться одной из следующих идей: &lt;br /&gt;
а) (&amp;lt;tex&amp;gt;O(n \cdot log^2(n)&amp;lt;/tex&amp;gt;) Бинарным поиском ищем ближайший слева и ближайший справа к i-му городу госпиталь (такой город &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, что &amp;lt;tex&amp;gt;\min\limits_{k=i..j}b_k= 1&amp;lt;/tex&amp;gt;). Для этого внутри бинарного поиска каждый раз делаем запрос на поиск минимума в дереве отрезков.  &lt;br /&gt;
б) (&amp;lt;tex&amp;gt;O(n \cdot log(n)&amp;lt;/tex&amp;gt;) Будем одним спуском/подъемом по дереву определять, куда нам нужно идти (в левое или правое поддерево), тем самым делая одновоременно и бинарный поиск, и спуск/подъем по дереву.&lt;br /&gt;
&lt;br /&gt;
== Статическая центроидная декомпозиция ==&lt;br /&gt;
Перейдем к обобщению поставленных задач на случай дерева. Начнем, как и полагается, с первой:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть взвешенное дерево &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин, в каждой вершине которого написаны положительные целые числа. Также по-прежнему даны числа &amp;lt;tex&amp;gt;W &amp;gt;= 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; вершин дерева, таких что расстояние между ними не превосходит &amp;lt;math&amp;gt;l&amp;lt;/math&amp;gt; по числу ребер и не превосходит &amp;lt;math&amp;gt;W&amp;lt;/math&amp;gt; по сумме весов.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Для решения новой задачи применим ту же идею, что и была до этого {{---}} разделяй и властвуй. Для этого нам потребуется следующий объект :&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|id=191213&lt;br /&gt;
|definition =&lt;br /&gt;
'''Центроидом дерева''' (англ. ''centroid'') называется такая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, после удаления которой дерево разбивается на несколько (&amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;) поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, таких что для каждого &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;: &amp;lt;tex&amp;gt;|t_i| \leqslant \frac{n}{2}&amp;lt;/tex&amp;gt;, т.е. размер каждого поддерева не превосходит половины размера исходного дерева.&lt;br /&gt;
}}&lt;br /&gt;
Итак, в случае дерева идея разделяй-и-властвуй из предыдущего пункта будет формулироваться так: найдем центроид (доказательство её существования и алгоритм нахождение см. далее). Предположим, что мы сумели найти центроид за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; {{---}} размер дерева. Тогда, как и в упрощенной версии задачи {{---}} рекурсивно найдем ответ для всех поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, после чего попытаемся найти недостающие пары вершин, находящиехя в разных поддеревьях и удовлетворяющих вопросу задачи. Для этого будем отвечать на следующие запросы: пусть мы сейчас считаем все пары, где первая из вершин находится в поддереве &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и мы в некоторой структуре данных &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; храним все вершины остальных деревьев (каждую вершину задаем парой &amp;lt;math&amp;gt;(depth(v), length(v))&amp;lt;/math&amp;gt; {{---}} глубина вершины и длина пути до нее из корня поддерева), расстояние до которых от корня их поддерева не превышает &amp;lt;math&amp;gt;min(l, n)&amp;lt;/math&amp;gt;. Тогда просто пройдемся по всем вершинам &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; поддерева &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и прибавим к ответу число вершин в структуре &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt;, таких, что &amp;lt;tex&amp;gt;depth(u) \leqslant l - depth(v)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;length(u) \leqslant l - length(v)&amp;lt;/tex&amp;gt;. Это двумерные запросы, на которые можно отвечать за &amp;lt;math&amp;gt;O(log^2(n)&amp;lt;/math&amp;gt; с помощью [[Многомерное_дерево_отрезков|2d-дерева отрезков]], либо за &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с помощью [[Перечисление_точек_в_произвольном_прямоугольнике_за_n_*_log_%5E(d_-_1)_n_(range_tree)|техники поиска точек в d-мерном пространстве]]. Также читателю предлагается придумать и более эффективные и простые способы решить эту подзадачу.&lt;br /&gt;
&lt;br /&gt;
Оценим итоговую асимптотику: &amp;lt;tex&amp;gt;T(n) = k * T(n / k) + O(n \cdot log(n))&amp;lt;/tex&amp;gt;. Решая это рекурентное соотношение, получим &amp;lt;math&amp;gt;O(n \cdot log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь, как и было обещено, докажем лемму о существовании центроида и опишем алгоритм его эффективного поиска.&lt;br /&gt;
&lt;br /&gt;
=== Лемма о существовании центроида и алгоритм его нахождения. ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
В любом дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; существует центроид.&lt;br /&gt;
|proof=&lt;br /&gt;
Рассмотрим корень дерева &amp;lt;math&amp;gt;(r)&amp;lt;/math&amp;gt;. Положим изначально &amp;lt;math&amp;gt;v = r&amp;lt;/math&amp;gt;. Изначально &amp;lt;math&amp;gt;|subtree(v)| = n&amp;lt;/math&amp;gt;. Среди всех детей &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; выберем вершину &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; с максимальным размером поддерева. Если &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид, то положим &amp;lt;math&amp;gt;v = u&amp;lt;/math&amp;gt; и продолжим выбор нового u, иначе {{---}} остановимся. Докажем, что мы в какой-то момент остановимся. Пусть в призвольный момент времени &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид и размер её наддерева меньше &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, значит максимальное поддерево имеет размер больше чем &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, т.е. &amp;lt;math&amp;gt;|subtree(u)| &amp;gt; \frac{n}{2}&amp;lt;/math&amp;gt;, а значит размер &amp;quot;наддерева&amp;quot; вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; равен &amp;lt;tex&amp;gt;n - |subtree(u)| &amp;lt; \frac{n}{2}&amp;lt;/tex&amp;gt;. При этом теперь размер любого поддерева, на которое распадется дерево t при удалении вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; не превосходит &amp;lt;math&amp;gt;|subtree(u)| - 1&amp;lt;/math&amp;gt;, т.к. наддерево имеет размер меньше, чем поддерево &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, а любое поддерево вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; имеет хотя бы на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; вершину меньше (сама вершина &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;). По индукции получаем, что в любой момент времени размер наддерева вершины v меньше &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, значит мы будем спускаться только вниз по дереву &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, и при переходе к вершине &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} сыну &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; размер максимального поддерева уменьшится как минимум на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Значит не более чем за &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; шагов наши действия прекратятся и мы окажемся в центроиде дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
Итак, мы конструктивно доказали существование центроида и привели линейный относительно размера дерева алгоритм его нахождения. &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Реализация ===&lt;br /&gt;
&lt;br /&gt;
Поиск центроида в дереве:&lt;br /&gt;
&lt;br /&gt;
  '''int''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int''' sz[n])&lt;br /&gt;
     max_subtree = -1&lt;br /&gt;
     '''for''' u : children(v)&lt;br /&gt;
          '''if''' sz[u] &amp;gt; sz[v] / 2 and sz[u] &amp;gt; sz[max_subtree] &amp;lt;font color=green&amp;gt;// считаем sz[-1] = 0 &amp;lt;/font &amp;gt;&lt;br /&gt;
               max_subtree = u&lt;br /&gt;
     '''if''' max_subtree == -1&lt;br /&gt;
          return v&lt;br /&gt;
     '''else''' &lt;br /&gt;
          '''return''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz)&lt;br /&gt;
&lt;br /&gt;
Шаблон решения произвольной задачи на статическую центроидную декомпозицию:&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int''' sz[n])&lt;br /&gt;
     c = &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz) &amp;lt;font color=green&amp;gt;// находим c {{---}} центроид поддерева вершины v &amp;lt;/font &amp;gt;&lt;br /&gt;
     ch = children[c]&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{deleteEdges}&amp;lt;/tex&amp;gt;(c, children) &amp;lt;font color=green&amp;gt;// удаляем все ребра между вершиной с и детьми, чтобы мы не смогли из детей попасть в с. &lt;br /&gt;
         Это полезно делать, если решение подзадачи для поддерева предполагает проход dfs &amp;lt;/font &amp;gt;&lt;br /&gt;
     '''for''' c2 : ch[c]&lt;br /&gt;
          &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;(children, c2, sz)&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{mergeSolution}&amp;lt;/tex&amp;gt;(children, c2) &amp;lt;font color=green&amp;gt;// решаем для текущего поддерева &amp;lt;/font &amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Динамическая центроидная декомпозиция (дерево центроидной декомпозиции) ==&lt;br /&gt;
Теперь вернемся ко второй задаче введения. Для ее решения мы пользовались динамичекой версией divide &amp;amp; conquer {{---}} деревом отрезков. В предыдущем пункте мы определили статический оналог divide &amp;amp; conquer для дерева. Теперь обобщим этот метод для динамических задач.&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Деревом центроидов (или центроидной декомпозицией)''' дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; называют дерево &amp;lt;math&amp;gt;T(t)&amp;lt;/math&amp;gt;, построенное на вершинах дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; следующим образом :&lt;br /&gt;
* Пусть вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} центроид дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Объявим его корнем нового дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пусть при удалении вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; из дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; оно распалось на поддеревья &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, тогда детьми вершины c объявим &amp;lt;tex&amp;gt;T(t_1), T(t_t),\dots, T(t_k)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
=== Свойства центроидной декомпозиции ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
'''Свойства центроидной декомпозиции''' :&lt;br /&gt;
* Глубина дерева центроидов не превосходит &amp;lt;tex&amp;gt;log(n)&amp;lt;/tex&amp;gt;. &lt;br /&gt;
* Каждая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; является центроидом одного из поддеревьев дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;&lt;br /&gt;
* Каждая вершина дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; поддеревьям дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (еще говорят, что вершина принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; центроидам дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, или что эти центроиды содержат вершину)&lt;br /&gt;
* Для любых вершин &amp;lt;tex&amp;gt;u, v \in T (u \neq v)&amp;lt;/tex&amp;gt; верно ровно одно из следующих трех утверждений: &lt;br /&gt;
a) &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
b) &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
c) &amp;lt;tex&amp;gt;T(u) \cap T(v) = \emptyset &amp;lt;/tex&amp;gt;&lt;br /&gt;
* Простой путь между любой парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; содержит центроид &amp;lt;tex&amp;gt;c \in T(t)&amp;lt;/tex&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
* Действительно, т.к. размер поддерева &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; не превосходит &amp;lt;tex&amp;gt;\frac{|subtree(c)|}{2}&amp;lt;/tex&amp;gt;, то при спуске в каждую следующую вершину на пути к любому листу в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; размер поддерева вершины, в которой мы сейчас находимся, уменьшается как минимум на &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;. Значит длина всего пути до листа не превосходит &amp;lt;math&amp;gt;log(n)&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
* Второе свойство очевидно из построения дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, т.к. если вершина принадлежит дереву центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то она является центроидом, а из построения &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы знаем, что каждая вершина исходного дерева принадлежит и дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Третье свойство {{---}} прямое следствие первых двух, т.к. вершина принадлежит любому центроиду &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; т.и т.т., когда c {{---}} отец вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов. Т.к. вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; точно принадлежит дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (свойство 2), то она лежит на каком-то пути в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, причем все ее родители (центроиды) ее содержат. А по свойству 1 длина любого вертикального (и даже простого) пути есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
* Четвертое свойство очевидно из того, что &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; {{---}} дерево. Т.к. &amp;lt;math&amp;gt;T(u)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;T(v)&amp;lt;/math&amp;gt; {{---}} поддеревья различных вершин дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то либо они не пересекаются, либо &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;, либо &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
* Для доказательства последнего свойства выберем в качестве вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;lca(u, v)&amp;lt;/math&amp;gt; в дереве центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Покажем, что так выбранная вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удовлетворяет заявленным свойствам. То, что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt; {{---}} очевидно по определению &amp;lt;math&amp;gt;lca&amp;lt;/math&amp;gt;, т.к. каждый предок любой вершины в дереве центроидов содержит эту вершину. Теперь докажем, что &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежит на пути между парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt;. Т.к. &amp;lt;math&amp;gt;c = lca(u, v)&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то из &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; нет ребра в такого сына, который содержит одновременно &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в своем поддереве (в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;), значит после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; разделится на несколько поддеревьев, таких что вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; окажутся в разных компонентах связности. А значит найдется такое ребро &amp;lt;math&amp;gt;(c, x)&amp;lt;/math&amp;gt;, которое принадлежало пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, но после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удалилось. Это доказывает то, что вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежала на пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
С помощью описанных свойств дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы можем решить задачу 2 для дерева :&lt;br /&gt;
&lt;br /&gt;
=== Пример решения задачи с помощью центроидной декомпозиции === &lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин. В каждый момент времени любая вершина дерева может быть либо помечена, либо нет. Изначально помечена только вершина номер &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Дан список из &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; запросов: &lt;br /&gt;
* Вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; пометили.&lt;br /&gt;
* С вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; сняли пометку.&lt;br /&gt;
* Найти номер ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Решение :&lt;br /&gt;
&lt;br /&gt;
Построим центроидную декомпозицию &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Изначально посчитаем для каждого центроида, содержащего вершину &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt; посчитаем расстояние до вершины &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Для этого воспользуемся [[Метод_двоичного_подъема|методом двоичных подъемов для поиска lca пары вершин в дереве]], а также тем фактом, что если глубина &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве определена как расстояние от корня до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то длина пути между парой вершин &amp;lt;math&amp;gt;(u, v)&amp;lt;/math&amp;gt; есть &amp;lt;tex&amp;gt;len(u, v) = h(u) + h(v) - 2 \cdot h(lca(u, v))&amp;lt;/tex&amp;gt;. Если изначально предпосчитать проходом [[Обход_в_глубину,_цвета_вершин| dfs]] величины &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, то ответ на запрос &amp;lt;math&amp;gt;len(u, v)&amp;lt;/math&amp;gt; можно делать за время &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с &amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt;доп. памятью. Также можно воспользоваться техникой [[Сведение_задачи_LCA_к_задаче_RMQ|сведения задачи LCA к RMQ]] и решить с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; времени на запрос. Теперь научимся отвечать на запросы. Из последнего свойства центроидной декомпозиции видно, что если &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} искомая ближайшая помеченная вершина к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то путь между ними содержит центроид &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;, причем &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок одновременно вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве&amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Поэтому заведем [[Красно-черное_дерево|двоичное дерево поиска]] для каждого центроида &amp;lt;math&amp;gt;c \in T&amp;lt;/math&amp;gt;. В этой структуре для каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; будем хранить пары &amp;lt;math&amp;gt;(len(c, u), u)&amp;lt;/math&amp;gt; для всех помеченных вершин &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в поддереве центроидов &amp;lt;math&amp;gt;T(c)&amp;lt;/math&amp;gt;. Когда приходит запрос пометить вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} добавим в структуру данных для всех предков &amp;lt;math&amp;gt;p_c&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; пары &amp;lt;math&amp;gt;(len(p_c, v), v)&amp;lt;/math&amp;gt;. Мы совершим &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; добавлений, затратив &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; действий на каждое. Запрос снятия пометки с вершины обрабатывается аналогичными удалениями. Запрос поиска ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины {{---}} это запрос поиска вершины u, такой что  величина &amp;lt;math&amp;gt;len(c, u) + len(c, v)&amp;lt;/math&amp;gt; минимальна, где &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов (по пятому свойству, нас интересуют именно такие &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;). Этот запрос занимает так же &amp;lt;tex&amp;gt;O(log^2(n))&amp;lt;/tex&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
Итак, мы научились решать задачу с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и временной сложностью &amp;lt;math&amp;gt;O(log^2(n))&amp;lt;/math&amp;gt; на запрос любого типа с предварительным предпосчетом за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Варианты хранения центроидной декомпозиции ==&lt;br /&gt;
Для хранения дерева центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; есть &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; подхода, имеющих свои преимущества и недостатки :&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева запомним величину &amp;lt;math&amp;gt;p_v&amp;lt;/math&amp;gt; {{---}} номер предка вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Этот подход наиболее экономный по памяти (&amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;), но уступает в скорости и функциональности.&lt;br /&gt;
&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева будем хранить весь массив предков &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt; в дереве центроидов.&lt;br /&gt;
&lt;br /&gt;
Этот подход уступает в количестве необходимой дополнительной памяти (&amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt; суммарно), но имеет ряд преимуществ :&lt;br /&gt;
&lt;br /&gt;
1) При проходе по массиву предков фиксированной вершины будет выигрыш в скорости работы, т.к. весь массив будет лежать непрерывным блоком данных и следовательно будет закэширован&lt;br /&gt;
&lt;br /&gt;
2) На массиве предков можно строить различные структуры данных (такие как, например, дерево отрезков) для быстрого (в случае с деревом отрезков &amp;lt;math&amp;gt;O(log(log(n)))&amp;lt;/math&amp;gt; на запрос) поиска предка с необходимыми свойствами. Так, например, в описанной выше задаче про помеченные вершины наибольшего общего предка можно искать методом двоичных подъемов за &amp;lt;math&amp;gt;O(log(log(n))&amp;lt;/math&amp;gt; на запрос, т.к. размер массива предков есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; (по свойству 1 центроидной декомпозиции). Используя эту оптимизацию можно получить время &amp;lt;math&amp;gt;O(log(n)) + O(log(log(n)) = O(log(n))&amp;lt;/math&amp;gt; на запрос нахождения ближайшей помеченной вершины. Чтобы добиться улучшенной асимптотики для запросов изменения можно хранить дерево отрезков на каждом из путей &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt;, в каждой вершине которого хранить двоичное дерево поиска и поддерживать отложенные операции. Тогда ответ на эти запросы будет занимать &amp;lt;math&amp;gt;O(log(n) \cdot log(log(n))&amp;lt;/math&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
&lt;br /&gt;
*[[:Дерево_отрезков._Построение|Дерево отрезков]]&lt;br /&gt;
*[[:Heavy-light_декомпозиция|Heavy-light декомпозиция]]&lt;br /&gt;
*[[:Метод_двоичного_подъема| Метод двоичного подъема]]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы и структуры данных]]&lt;/div&gt;</summary>
		<author><name>92.255.113.52</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61627</id>
		<title>Centroid decomposition</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61627"/>
				<updated>2017-06-14T21:34:12Z</updated>
		
		<summary type="html">&lt;p&gt;92.255.113.52: /* Реализация */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Centroid decomposition (рус. центроидная декомпозиция) {{---}} это структура данных, позволяющая отвечать на запросы на дереве. Чаще всего это запросы, связанные с нахождением функции на вершинах, связанных неравенством на расстояние между ними в дереве. Также иногда применяется для запросов на путях в дереве.&lt;br /&gt;
&lt;br /&gt;
== Введение ==&lt;br /&gt;
Рассмотрим &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; задачи на обычном массиве (в дальнейшем мы будем их обобщать на случай дерева):&lt;br /&gt;
&lt;br /&gt;
Задача &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть массив &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; положительных целых чисел из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов и числа &amp;lt;tex&amp;gt;W \geqslant 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;рl&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; индексов массива, таких что &amp;lt;tex&amp;gt;|j - i| \leqslant l &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\sum_{i=0}^{n - 1} a_i \leqslant W&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
Задача &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть прямая дорога, на которой расположены &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; городов. В некоторых городах есть госпитали, которые могут принимать больных. Поступают запросы вида :&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;, в котором находится больной и требуется найти такой город &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;|u - v|&amp;lt;/tex&amp;gt; минимально возможное.&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что больше он не будет принимать больных&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что теперь он может принимать больных&lt;br /&gt;
}}&lt;br /&gt;
Для начала решим обе задачи. &lt;br /&gt;
Первая задача решается методом [[Сортировка_слиянием|divide &amp;amp; conquer (рус. разделяй и властвуй)]] {{---}} давайте разделим массив &amp;lt;tex&amp;gt;a[0 \dots n-1]&amp;lt;/tex&amp;gt; на 2 массива &amp;lt;tex&amp;gt;a[0\dots\frac{n}{2} - 1]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;a[\frac{n}{2} \dots n-1]&amp;lt;/tex&amp;gt; и рекурсивно решим задачу для каждого из них. Осталось научиться находить количество искомых пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt;, таких что &amp;lt;tex&amp;gt;i &amp;lt; \frac{n}{2}, j \geqslant \frac{n}{2}&amp;lt;/tex&amp;gt;. Для этого воспользуемся другой известной техникой {{---}} методом двух указателей. Посчитаем массив префиксных сумм для правой половины &amp;lt;tex&amp;gt;pref[i] = \sum_{j=\frac{n}{2}}^{i} a_j&amp;lt;/tex&amp;gt; и суффиксных (&amp;lt;tex&amp;gt;suf[i] = \sum_{j=i}^{\frac{n}{2} + 1} a_j&amp;lt;/tex&amp;gt;) {{---}} для левой. Заведем два указателя (&amp;lt;tex&amp;gt;p_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;p_2&amp;lt;/tex&amp;gt;). Изначально установим &amp;lt;tex&amp;gt;p_1 = \frac{n}{2} - l + 1, p_2 = \frac{n}{2}&amp;lt;/tex&amp;gt;. Пока &amp;lt;tex&amp;gt;p_2 - 1&amp;gt; \frac{n}{2}&amp;lt;/tex&amp;gt; и&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;pref[p_2] + suf[p_1] &amp;gt; W &amp;lt;/tex&amp;gt; будем уменьшать &amp;lt;math&amp;gt;p_2&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Если после этого &amp;lt;math&amp;gt;pref[p_2] + suf[p_1] \leqslant W&amp;lt;/math&amp;gt;, то к ответу прибавим &amp;lt;math&amp;gt;(p_2 - \frac{n}{2} + 1) * (\frac{n}{2} - p_1)&amp;lt;/math&amp;gt;, посго, увеличим &amp;lt;math&amp;gt;p_1&amp;lt;/math&amp;gt; на &amp;lt;math/math&amp;gt;. Так будем делать, пока &amp;lt;math&amp;gt;p_1 &amp;lt; \frac{n}{2}&amp;lt;/math&amp;gt;. В конце сложим текущий ответ и ответы для половин массива {{---}} получим ответ на задачу. Асимптотика такого алгоритма: &amp;lt;tex&amp;gt;T(n) = 2 * T(n / 2) + O(n) = O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вторая задача имеет запросы на изменение и поэтому надо применить динамическую версию divide &amp;amp; conquer {{---}} [[Дерево_отрезков._Построение|дерево отрезков]]. Построим дерево отрезков,&lt;br /&gt;
поддерживающее 2 вида запросов: присвоение в точке и минимум на отрезке. Изначально сделаем так, чтобы дереву отрезков соответствовал массив &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;b_i = 0&amp;lt;/tex&amp;gt;, если в i-м городе принимает госпиталь и &amp;lt;tex&amp;gt;b_i = 1&amp;lt;/tex&amp;gt; иначе. Когда в каком-то городе открывается/закрывается госпиталь {{---}} делаем запрос на изменение в дереве отрезков. Когда требуется узнать ближайщий госпиталь к &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-му городу, можно воспользоваться одной из следующих идей: &lt;br /&gt;
а) (&amp;lt;tex&amp;gt;O(n \cdot log^2(n)&amp;lt;/tex&amp;gt;) Бинарным поиском ищем ближайший слева и ближайший справа к i-му городу госпиталь (такой город &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, что &amp;lt;tex&amp;gt;\min\limits_{k=i..j}b_k= 1&amp;lt;/tex&amp;gt;). Для этого внутри бинарного поиска каждый раз делаем запрос на поиск минимума в дереве отрезков.  &lt;br /&gt;
б) (&amp;lt;tex&amp;gt;O(n \cdot log(n)&amp;lt;/tex&amp;gt;) Будем одним спуском/подъемом по дереву определять, куда нам нужно идти (в левое или правое поддерево), тем самым делая одновоременно и бинарный поиск, и спуск/подъем по дереву.&lt;br /&gt;
&lt;br /&gt;
== Статическая центроидная декомпозиция ==&lt;br /&gt;
Перейдем к обобщению поставленных задач на случай дерева. Начнем, как и полагается, с первой:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть взвешенное дерево &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин, в каждой вершине которого написаны положительные целые числа. Также по-прежнему даны числа &amp;lt;tex&amp;gt;W &amp;gt;= 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; вершин дерева, таких что расстояние между ними не превосходит &amp;lt;math&amp;gt;l&amp;lt;/math&amp;gt; по числу ребер и не превосходит &amp;lt;math&amp;gt;W&amp;lt;/math&amp;gt; по сумме весов.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Для решения новой задачи применим ту же идею, что и была до этого {{---}} разделяй и властвуй. Для этого нам потребуется следующий объект :&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|id=191213&lt;br /&gt;
|definition =&lt;br /&gt;
'''Центроидом дерева''' (англ. ''centroid'') называется такая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, после удаления которой дерево разбивается на несколько (&amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;) поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, таких что для каждого &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;: &amp;lt;tex&amp;gt;|t_i| \leqslant \frac{n}{2}&amp;lt;/tex&amp;gt;, т.е. размер каждого поддерева не превосходит половины размера исходного дерева.&lt;br /&gt;
}}&lt;br /&gt;
Итак, в случае дерева идея разделяй-и-властвуй из предыдущего пункта будет формулироваться так: найдем центроид (доказательство её существования и алгоритм нахождение см. далее). Предположим, что мы сумели найти центроид за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; {{---}} размер дерева. Тогда, как и в упрощенной версии задачи {{---}} рекурсивно найдем ответ для всех поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, после чего попытаемся найти недостающие пары вершин, находящиехя в разных поддеревьях и удовлетворяющих вопросу задачи. Для этого будем отвечать на следующие запросы: пусть мы сейчас считаем все пары, где первая из вершин находится в поддереве &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и мы в некоторой структуре данных &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; храним все вершины остальных деревьев (каждую вершину задаем парой &amp;lt;math&amp;gt;(depth(v), length(v))&amp;lt;/math&amp;gt; {{---}} глубина вершины и длина пути до нее из корня поддерева), расстояние до которых от корня их поддерева не превышает &amp;lt;math&amp;gt;min(l, n)&amp;lt;/math&amp;gt;. Тогда просто пройдемся по всем вершинам &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; поддерева &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и прибавим к ответу число вершин в структуре &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt;, таких, что &amp;lt;tex&amp;gt;depth(u) \leqslant l - depth(v)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;length(u) \leqslant l - length(v)&amp;lt;/tex&amp;gt;. Это двумерные запросы, на которые можно отвечать за &amp;lt;math&amp;gt;O(log^2(n)&amp;lt;/math&amp;gt; с помощью [[Многомерное_дерево_отрезков|2d-дерева отрезков]], либо за &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с помощью [[Перечисление_точек_в_произвольном_прямоугольнике_за_n_*_log_%5E(d_-_1)_n_(range_tree)|техники поиска точек в d-мерном пространстве]]. Также читателю предлагается придумать и более эффективные и простые способы решить эту подзадачу.&lt;br /&gt;
&lt;br /&gt;
Оценим итоговую асимптотику: &amp;lt;tex&amp;gt;T(n) = k * T(n / k) + O(n \cdot log(n))&amp;lt;/tex&amp;gt;. Решая это рекурентное соотношение, получим &amp;lt;math&amp;gt;O(n \cdot log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь, как и было обещено, докажем лемму о существовании центроида и опишем алгоритм его эффективного поиска.&lt;br /&gt;
&lt;br /&gt;
=== Лемма о существовании центроида и алгоритм его нахождения. ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
В любом дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; существует центроид.&lt;br /&gt;
|proof=&lt;br /&gt;
Рассмотрим корень дерева &amp;lt;math&amp;gt;(r)&amp;lt;/math&amp;gt;. Положим изначально &amp;lt;math&amp;gt;v = r&amp;lt;/math&amp;gt;. Изначально &amp;lt;math&amp;gt;|subtree(v)| = n&amp;lt;/math&amp;gt;. Среди всех детей &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; выберем вершину &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; с максимальным размером поддерева. Если &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид, то положим &amp;lt;math&amp;gt;v = u&amp;lt;/math&amp;gt; и продолжим выбор нового u, иначе {{---}} остановимся. Докажем, что мы в какой-то момент остановимся. Пусть в призвольный момент времени &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид и размер её наддерева меньше &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, значит максимальное поддерево имеет размер больше чем &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, т.е. &amp;lt;math&amp;gt;|subtree(u)| &amp;gt; \frac{n}{2}&amp;lt;/math&amp;gt;, а значит размер &amp;quot;наддерева&amp;quot; вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; равен &amp;lt;tex&amp;gt;n - |subtree(u)| &amp;lt; \frac{n}{2}&amp;lt;/tex&amp;gt;. При этом теперь размер любого поддерева, на которое распадется дерево t при удалении вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; не превосходит &amp;lt;math&amp;gt;|subtree(u)| - 1&amp;lt;/math&amp;gt;, т.к. наддерево имеет размер меньше, чем поддерево &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, а любое поддерево вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; имеет хотя бы на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; вершину меньше (сама вершина &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;). По индукции получаем, что в любой момент времени размер наддерева вершины v меньше &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, значит мы будем спускаться только вниз по дереву &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, и при переходе к вершине &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} сыну &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; размер максимального поддерева уменьшится как минимум на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Значит не более чем за &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; шагов наши действия прекратятся и мы окажемся в центроиде дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
Итак, мы конструктивно доказали существование центроида и привели линейный относительно размера дерева алгоритм его нахождения. &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Реализация ===&lt;br /&gt;
&lt;br /&gt;
Поиск центроида в дереве:&lt;br /&gt;
&lt;br /&gt;
  '''int''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int''' sz[n])&lt;br /&gt;
     max_subtree = -1&lt;br /&gt;
     '''for''' u : children(v)&lt;br /&gt;
          '''if''' sz[u] &amp;gt; sz[v] / 2 and sz[u] &amp;gt; sz[max_subtree] &amp;lt;font color=green&amp;gt;// считаем sz[-1] = 0 &amp;lt;/font &amp;gt;&lt;br /&gt;
               max_subtree = u&lt;br /&gt;
     '''if''' max_subtree == -1&lt;br /&gt;
          return v&lt;br /&gt;
     '''else''' &lt;br /&gt;
          '''return''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz)&lt;br /&gt;
&lt;br /&gt;
Шаблон решения произвольной задачи на статическую центроидную декомпозицию:&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int''' sz[n])&lt;br /&gt;
     c = &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz) &amp;lt;font color=green&amp;gt;// находим c {{---}} центроид поддерева вершины v &amp;lt;/font &amp;gt;&lt;br /&gt;
     ch = children[c]&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{deleteEdges}&amp;lt;/tex&amp;gt;(c, children) &amp;lt;font color=green&amp;gt;// удаляем все ребра между вершиной с и детьми, чтобы мы не смогли из детей попасть в с. &lt;br /&gt;
     Это полезно делать, если решение  подзадачи для поддерева предполагает проход dfs &amp;lt;/font &amp;gt;&lt;br /&gt;
     '''for''' c2 : ch[c]&lt;br /&gt;
          &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;(children, c2, sz)&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{mergeSolution}&amp;lt;/tex&amp;gt;(children, c2) &amp;lt;font color=green&amp;gt;// решаем для текущего поддерева &amp;lt;/font &amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Динамическая центроидная декомпозиция (дерево центроидной декомпозиции) ==&lt;br /&gt;
Теперь вернемся ко второй задаче введения. Для ее решения мы пользовались динамичекой версией divide &amp;amp; conquer {{---}} деревом отрезков. В предыдущем пункте мы определили статический оналог divide &amp;amp; conquer для дерева. Теперь обобщим этот метод для динамических задач.&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Деревом центроидов (или центроидной декомпозицией)''' дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; называют дерево &amp;lt;math&amp;gt;T(t)&amp;lt;/math&amp;gt;, построенное на вершинах дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; следующим образом :&lt;br /&gt;
* Пусть вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} центроид дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Объявим его корнем нового дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пусть при удалении вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; из дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; оно распалось на поддеревья &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, тогда детьми вершины c объявим &amp;lt;tex&amp;gt;T(t_1), T(t_t),\dots, T(t_k)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
=== Свойства центроидной декомпозиции ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
'''Свойства центроидной декомпозиции''' :&lt;br /&gt;
* Глубина дерева центроидов не превосходит &amp;lt;tex&amp;gt;log(n)&amp;lt;/tex&amp;gt;. &lt;br /&gt;
* Каждая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; является центроидом одного из поддеревьев дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;&lt;br /&gt;
* Каждая вершина дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; поддеревьям дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (еще говорят, что вершина принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; центроидам дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, или что эти центроиды содержат вершину)&lt;br /&gt;
* Для любых вершин &amp;lt;tex&amp;gt;u, v \in T (u \neq v)&amp;lt;/tex&amp;gt; верно ровно одно из следующих трех утверждений: &lt;br /&gt;
a) &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
b) &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
c) &amp;lt;tex&amp;gt;T(u) \cap T(v) = \emptyset &amp;lt;/tex&amp;gt;&lt;br /&gt;
* Простой путь между любой парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; содержит центроид &amp;lt;tex&amp;gt;c \in T(t)&amp;lt;/tex&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
* Действительно, т.к. размер поддерева &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; не превосходит &amp;lt;tex&amp;gt;\frac{|subtree(c)|}{2}&amp;lt;/tex&amp;gt;, то при спуске в каждую следующую вершину на пути к любому листу в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; размер поддерева вершины, в которой мы сейчас находимся, уменьшается как минимум на &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;. Значит длина всего пути до листа не превосходит &amp;lt;math&amp;gt;log(n)&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
* Второе свойство очевидно из построения дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, т.к. если вершина принадлежит дереву центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то она является центроидом, а из построения &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы знаем, что каждая вершина исходного дерева принадлежит и дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Третье свойство {{---}} прямое следствие первых двух, т.к. вершина принадлежит любому центроиду &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; т.и т.т., когда c {{---}} отец вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов. Т.к. вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; точно принадлежит дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (свойство 2), то она лежит на каком-то пути в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, причем все ее родители (центроиды) ее содержат. А по свойству 1 длина любого вертикального (и даже простого) пути есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
* Четвертое свойство очевидно из того, что &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; {{---}} дерево. Т.к. &amp;lt;math&amp;gt;T(u)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;T(v)&amp;lt;/math&amp;gt; {{---}} поддеревья различных вершин дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то либо они не пересекаются, либо &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;, либо &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
* Для доказательства последнего свойства выберем в качестве вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;lca(u, v)&amp;lt;/math&amp;gt; в дереве центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Покажем, что так выбранная вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удовлетворяет заявленным свойствам. То, что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt; {{---}} очевидно по определению &amp;lt;math&amp;gt;lca&amp;lt;/math&amp;gt;, т.к. каждый предок любой вершины в дереве центроидов содержит эту вершину. Теперь докажем, что &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежит на пути между парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt;. Т.к. &amp;lt;math&amp;gt;c = lca(u, v)&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то из &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; нет ребра в такого сына, который содержит одновременно &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в своем поддереве (в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;), значит после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; разделится на несколько поддеревьев, таких что вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; окажутся в разных компонентах связности. А значит найдется такое ребро &amp;lt;math&amp;gt;(c, x)&amp;lt;/math&amp;gt;, которое принадлежало пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, но после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удалилось. Это доказывает то, что вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежала на пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
С помощью описанных свойств дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы можем решить задачу 2 для дерева :&lt;br /&gt;
&lt;br /&gt;
=== Пример решения задачи с помощью центроидной декомпозиции === &lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин. В каждый момент времени любая вершина дерева может быть либо помечена, либо нет. Изначально помечена только вершина номер &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Дан список из &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; запросов: &lt;br /&gt;
* Вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; пометили.&lt;br /&gt;
* С вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; сняли пометку.&lt;br /&gt;
* Найти номер ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Решение :&lt;br /&gt;
&lt;br /&gt;
Построим центроидную декомпозицию &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Изначально посчитаем для каждого центроида, содержащего вершину &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt; посчитаем расстояние до вершины &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Для этого воспользуемся [[Метод_двоичного_подъема|методом двоичных подъемов для поиска lca пары вершин в дереве]], а также тем фактом, что если глубина &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве определена как расстояние от корня до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то длина пути между парой вершин &amp;lt;math&amp;gt;(u, v)&amp;lt;/math&amp;gt; есть &amp;lt;tex&amp;gt;len(u, v) = h(u) + h(v) - 2 \cdot h(lca(u, v))&amp;lt;/tex&amp;gt;. Если изначально предпосчитать проходом [[Обход_в_глубину,_цвета_вершин| dfs]] величины &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, то ответ на запрос &amp;lt;math&amp;gt;len(u, v)&amp;lt;/math&amp;gt; можно делать за время &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с &amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt;доп. памятью. Также можно воспользоваться техникой [[Сведение_задачи_LCA_к_задаче_RMQ|сведения задачи LCA к RMQ]] и решить с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; времени на запрос. Теперь научимся отвечать на запросы. Из последнего свойства центроидной декомпозиции видно, что если &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} искомая ближайшая помеченная вершина к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то путь между ними содержит центроид &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;, причем &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок одновременно вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве&amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Поэтому заведем [[Красно-черное_дерево|двоичное дерево поиска]] для каждого центроида &amp;lt;math&amp;gt;c \in T&amp;lt;/math&amp;gt;. В этой структуре для каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; будем хранить пары &amp;lt;math&amp;gt;(len(c, u), u)&amp;lt;/math&amp;gt; для всех помеченных вершин &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в поддереве центроидов &amp;lt;math&amp;gt;T(c)&amp;lt;/math&amp;gt;. Когда приходит запрос пометить вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} добавим в структуру данных для всех предков &amp;lt;math&amp;gt;p_c&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; пары &amp;lt;math&amp;gt;(len(p_c, v), v)&amp;lt;/math&amp;gt;. Мы совершим &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; добавлений, затратив &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; действий на каждое. Запрос снятия пометки с вершины обрабатывается аналогичными удалениями. Запрос поиска ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины {{---}} это запрос поиска вершины u, такой что  величина &amp;lt;math&amp;gt;len(c, u) + len(c, v)&amp;lt;/math&amp;gt; минимальна, где &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов (по пятому свойству, нас интересуют именно такие &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;). Этот запрос занимает так же &amp;lt;tex&amp;gt;O(log^2(n))&amp;lt;/tex&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
Итак, мы научились решать задачу с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и временной сложностью &amp;lt;math&amp;gt;O(log^2(n))&amp;lt;/math&amp;gt; на запрос любого типа с предварительным предпосчетом за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Варианты хранения центроидной декомпозиции ==&lt;br /&gt;
Для хранения дерева центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; есть &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; подхода, имеющих свои преимущества и недостатки :&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева запомним величину &amp;lt;math&amp;gt;p_v&amp;lt;/math&amp;gt; {{---}} номер предка вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Этот подход наиболее экономный по памяти (&amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;), но уступает в скорости и функциональности.&lt;br /&gt;
&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева будем хранить весь массив предков &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt; в дереве центроидов.&lt;br /&gt;
&lt;br /&gt;
Этот подход уступает в количестве необходимой дополнительной памяти (&amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt; суммарно), но имеет ряд преимуществ :&lt;br /&gt;
&lt;br /&gt;
1) При проходе по массиву предков фиксированной вершины будет выигрыш в скорости работы, т.к. весь массив будет лежать непрерывным блоком данных и следовательно будет закэширован&lt;br /&gt;
&lt;br /&gt;
2) На массиве предков можно строить различные структуры данных (такие как, например, дерево отрезков) для быстрого (в случае с деревом отрезков &amp;lt;math&amp;gt;O(log(log(n)))&amp;lt;/math&amp;gt; на запрос) поиска предка с необходимыми свойствами. Так, например, в описанной выше задаче про помеченные вершины наибольшего общего предка можно искать методом двоичных подъемов за &amp;lt;math&amp;gt;O(log(log(n))&amp;lt;/math&amp;gt; на запрос, т.к. размер массива предков есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; (по свойству 1 центроидной декомпозиции). Используя эту оптимизацию можно получить время &amp;lt;math&amp;gt;O(log(n)) + O(log(log(n)) = O(log(n))&amp;lt;/math&amp;gt; на запрос нахождения ближайшей помеченной вершины. Чтобы добиться улучшенной асимптотики для запросов изменения можно хранить дерево отрезков на каждом из путей &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt;, в каждой вершине которого хранить двоичное дерево поиска и поддерживать отложенные операции. Тогда ответ на эти запросы будет занимать &amp;lt;math&amp;gt;O(log(n) \cdot log(log(n))&amp;lt;/math&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
&lt;br /&gt;
*[[:Дерево_отрезков._Построение|Дерево отрезков]]&lt;br /&gt;
*[[:Heavy-light_декомпозиция|Heavy-light декомпозиция]]&lt;br /&gt;
*[[:Метод_двоичного_подъема| Метод двоичного подъема]]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы и структуры данных]]&lt;/div&gt;</summary>
		<author><name>92.255.113.52</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61626</id>
		<title>Centroid decomposition</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61626"/>
				<updated>2017-06-14T21:33:58Z</updated>
		
		<summary type="html">&lt;p&gt;92.255.113.52: /* Реализация */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Centroid decomposition (рус. центроидная декомпозиция) {{---}} это структура данных, позволяющая отвечать на запросы на дереве. Чаще всего это запросы, связанные с нахождением функции на вершинах, связанных неравенством на расстояние между ними в дереве. Также иногда применяется для запросов на путях в дереве.&lt;br /&gt;
&lt;br /&gt;
== Введение ==&lt;br /&gt;
Рассмотрим &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; задачи на обычном массиве (в дальнейшем мы будем их обобщать на случай дерева):&lt;br /&gt;
&lt;br /&gt;
Задача &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть массив &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; положительных целых чисел из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов и числа &amp;lt;tex&amp;gt;W \geqslant 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;рl&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; индексов массива, таких что &amp;lt;tex&amp;gt;|j - i| \leqslant l &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\sum_{i=0}^{n - 1} a_i \leqslant W&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
Задача &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть прямая дорога, на которой расположены &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; городов. В некоторых городах есть госпитали, которые могут принимать больных. Поступают запросы вида :&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;, в котором находится больной и требуется найти такой город &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;|u - v|&amp;lt;/tex&amp;gt; минимально возможное.&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что больше он не будет принимать больных&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что теперь он может принимать больных&lt;br /&gt;
}}&lt;br /&gt;
Для начала решим обе задачи. &lt;br /&gt;
Первая задача решается методом [[Сортировка_слиянием|divide &amp;amp; conquer (рус. разделяй и властвуй)]] {{---}} давайте разделим массив &amp;lt;tex&amp;gt;a[0 \dots n-1]&amp;lt;/tex&amp;gt; на 2 массива &amp;lt;tex&amp;gt;a[0\dots\frac{n}{2} - 1]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;a[\frac{n}{2} \dots n-1]&amp;lt;/tex&amp;gt; и рекурсивно решим задачу для каждого из них. Осталось научиться находить количество искомых пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt;, таких что &amp;lt;tex&amp;gt;i &amp;lt; \frac{n}{2}, j \geqslant \frac{n}{2}&amp;lt;/tex&amp;gt;. Для этого воспользуемся другой известной техникой {{---}} методом двух указателей. Посчитаем массив префиксных сумм для правой половины &amp;lt;tex&amp;gt;pref[i] = \sum_{j=\frac{n}{2}}^{i} a_j&amp;lt;/tex&amp;gt; и суффиксных (&amp;lt;tex&amp;gt;suf[i] = \sum_{j=i}^{\frac{n}{2} + 1} a_j&amp;lt;/tex&amp;gt;) {{---}} для левой. Заведем два указателя (&amp;lt;tex&amp;gt;p_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;p_2&amp;lt;/tex&amp;gt;). Изначально установим &amp;lt;tex&amp;gt;p_1 = \frac{n}{2} - l + 1, p_2 = \frac{n}{2}&amp;lt;/tex&amp;gt;. Пока &amp;lt;tex&amp;gt;p_2 - 1&amp;gt; \frac{n}{2}&amp;lt;/tex&amp;gt; и&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;pref[p_2] + suf[p_1] &amp;gt; W &amp;lt;/tex&amp;gt; будем уменьшать &amp;lt;math&amp;gt;p_2&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Если после этого &amp;lt;math&amp;gt;pref[p_2] + suf[p_1] \leqslant W&amp;lt;/math&amp;gt;, то к ответу прибавим &amp;lt;math&amp;gt;(p_2 - \frac{n}{2} + 1) * (\frac{n}{2} - p_1)&amp;lt;/math&amp;gt;, посго, увеличим &amp;lt;math&amp;gt;p_1&amp;lt;/math&amp;gt; на &amp;lt;math/math&amp;gt;. Так будем делать, пока &amp;lt;math&amp;gt;p_1 &amp;lt; \frac{n}{2}&amp;lt;/math&amp;gt;. В конце сложим текущий ответ и ответы для половин массива {{---}} получим ответ на задачу. Асимптотика такого алгоритма: &amp;lt;tex&amp;gt;T(n) = 2 * T(n / 2) + O(n) = O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вторая задача имеет запросы на изменение и поэтому надо применить динамическую версию divide &amp;amp; conquer {{---}} [[Дерево_отрезков._Построение|дерево отрезков]]. Построим дерево отрезков,&lt;br /&gt;
поддерживающее 2 вида запросов: присвоение в точке и минимум на отрезке. Изначально сделаем так, чтобы дереву отрезков соответствовал массив &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;b_i = 0&amp;lt;/tex&amp;gt;, если в i-м городе принимает госпиталь и &amp;lt;tex&amp;gt;b_i = 1&amp;lt;/tex&amp;gt; иначе. Когда в каком-то городе открывается/закрывается госпиталь {{---}} делаем запрос на изменение в дереве отрезков. Когда требуется узнать ближайщий госпиталь к &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-му городу, можно воспользоваться одной из следующих идей: &lt;br /&gt;
а) (&amp;lt;tex&amp;gt;O(n \cdot log^2(n)&amp;lt;/tex&amp;gt;) Бинарным поиском ищем ближайший слева и ближайший справа к i-му городу госпиталь (такой город &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, что &amp;lt;tex&amp;gt;\min\limits_{k=i..j}b_k= 1&amp;lt;/tex&amp;gt;). Для этого внутри бинарного поиска каждый раз делаем запрос на поиск минимума в дереве отрезков.  &lt;br /&gt;
б) (&amp;lt;tex&amp;gt;O(n \cdot log(n)&amp;lt;/tex&amp;gt;) Будем одним спуском/подъемом по дереву определять, куда нам нужно идти (в левое или правое поддерево), тем самым делая одновоременно и бинарный поиск, и спуск/подъем по дереву.&lt;br /&gt;
&lt;br /&gt;
== Статическая центроидная декомпозиция ==&lt;br /&gt;
Перейдем к обобщению поставленных задач на случай дерева. Начнем, как и полагается, с первой:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть взвешенное дерево &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин, в каждой вершине которого написаны положительные целые числа. Также по-прежнему даны числа &amp;lt;tex&amp;gt;W &amp;gt;= 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; вершин дерева, таких что расстояние между ними не превосходит &amp;lt;math&amp;gt;l&amp;lt;/math&amp;gt; по числу ребер и не превосходит &amp;lt;math&amp;gt;W&amp;lt;/math&amp;gt; по сумме весов.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Для решения новой задачи применим ту же идею, что и была до этого {{---}} разделяй и властвуй. Для этого нам потребуется следующий объект :&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|id=191213&lt;br /&gt;
|definition =&lt;br /&gt;
'''Центроидом дерева''' (англ. ''centroid'') называется такая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, после удаления которой дерево разбивается на несколько (&amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;) поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, таких что для каждого &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;: &amp;lt;tex&amp;gt;|t_i| \leqslant \frac{n}{2}&amp;lt;/tex&amp;gt;, т.е. размер каждого поддерева не превосходит половины размера исходного дерева.&lt;br /&gt;
}}&lt;br /&gt;
Итак, в случае дерева идея разделяй-и-властвуй из предыдущего пункта будет формулироваться так: найдем центроид (доказательство её существования и алгоритм нахождение см. далее). Предположим, что мы сумели найти центроид за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; {{---}} размер дерева. Тогда, как и в упрощенной версии задачи {{---}} рекурсивно найдем ответ для всех поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, после чего попытаемся найти недостающие пары вершин, находящиехя в разных поддеревьях и удовлетворяющих вопросу задачи. Для этого будем отвечать на следующие запросы: пусть мы сейчас считаем все пары, где первая из вершин находится в поддереве &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и мы в некоторой структуре данных &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; храним все вершины остальных деревьев (каждую вершину задаем парой &amp;lt;math&amp;gt;(depth(v), length(v))&amp;lt;/math&amp;gt; {{---}} глубина вершины и длина пути до нее из корня поддерева), расстояние до которых от корня их поддерева не превышает &amp;lt;math&amp;gt;min(l, n)&amp;lt;/math&amp;gt;. Тогда просто пройдемся по всем вершинам &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; поддерева &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и прибавим к ответу число вершин в структуре &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt;, таких, что &amp;lt;tex&amp;gt;depth(u) \leqslant l - depth(v)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;length(u) \leqslant l - length(v)&amp;lt;/tex&amp;gt;. Это двумерные запросы, на которые можно отвечать за &amp;lt;math&amp;gt;O(log^2(n)&amp;lt;/math&amp;gt; с помощью [[Многомерное_дерево_отрезков|2d-дерева отрезков]], либо за &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с помощью [[Перечисление_точек_в_произвольном_прямоугольнике_за_n_*_log_%5E(d_-_1)_n_(range_tree)|техники поиска точек в d-мерном пространстве]]. Также читателю предлагается придумать и более эффективные и простые способы решить эту подзадачу.&lt;br /&gt;
&lt;br /&gt;
Оценим итоговую асимптотику: &amp;lt;tex&amp;gt;T(n) = k * T(n / k) + O(n \cdot log(n))&amp;lt;/tex&amp;gt;. Решая это рекурентное соотношение, получим &amp;lt;math&amp;gt;O(n \cdot log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь, как и было обещено, докажем лемму о существовании центроида и опишем алгоритм его эффективного поиска.&lt;br /&gt;
&lt;br /&gt;
=== Лемма о существовании центроида и алгоритм его нахождения. ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
В любом дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; существует центроид.&lt;br /&gt;
|proof=&lt;br /&gt;
Рассмотрим корень дерева &amp;lt;math&amp;gt;(r)&amp;lt;/math&amp;gt;. Положим изначально &amp;lt;math&amp;gt;v = r&amp;lt;/math&amp;gt;. Изначально &amp;lt;math&amp;gt;|subtree(v)| = n&amp;lt;/math&amp;gt;. Среди всех детей &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; выберем вершину &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; с максимальным размером поддерева. Если &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид, то положим &amp;lt;math&amp;gt;v = u&amp;lt;/math&amp;gt; и продолжим выбор нового u, иначе {{---}} остановимся. Докажем, что мы в какой-то момент остановимся. Пусть в призвольный момент времени &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид и размер её наддерева меньше &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, значит максимальное поддерево имеет размер больше чем &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, т.е. &amp;lt;math&amp;gt;|subtree(u)| &amp;gt; \frac{n}{2}&amp;lt;/math&amp;gt;, а значит размер &amp;quot;наддерева&amp;quot; вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; равен &amp;lt;tex&amp;gt;n - |subtree(u)| &amp;lt; \frac{n}{2}&amp;lt;/tex&amp;gt;. При этом теперь размер любого поддерева, на которое распадется дерево t при удалении вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; не превосходит &amp;lt;math&amp;gt;|subtree(u)| - 1&amp;lt;/math&amp;gt;, т.к. наддерево имеет размер меньше, чем поддерево &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, а любое поддерево вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; имеет хотя бы на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; вершину меньше (сама вершина &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;). По индукции получаем, что в любой момент времени размер наддерева вершины v меньше &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, значит мы будем спускаться только вниз по дереву &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, и при переходе к вершине &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} сыну &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; размер максимального поддерева уменьшится как минимум на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Значит не более чем за &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; шагов наши действия прекратятся и мы окажемся в центроиде дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
Итак, мы конструктивно доказали существование центроида и привели линейный относительно размера дерева алгоритм его нахождения. &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Реализация ===&lt;br /&gt;
&lt;br /&gt;
Поиск центроида в дереве:&lt;br /&gt;
&lt;br /&gt;
  '''int''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int''' sz[n])&lt;br /&gt;
     max_subtree = -1&lt;br /&gt;
     '''for''' u : children(v)&lt;br /&gt;
          '''if''' sz[u] &amp;gt; sz[v] / 2 and sz[u] &amp;gt; sz[max_subtree] &amp;lt;font color=green&amp;gt;// считаем sz[-1] = 0 &amp;lt;/font &amp;gt;&lt;br /&gt;
               max_subtree = u&lt;br /&gt;
     '''if''' max_subtree == -1&lt;br /&gt;
          return v&lt;br /&gt;
     '''else''' &lt;br /&gt;
          '''return''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz)&lt;br /&gt;
&lt;br /&gt;
Шаблон решения произвольной задачи на статическую центроидную декомпозицию:&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int''' sz[n])&lt;br /&gt;
     c = &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz) &amp;lt;font color=green&amp;gt;// находим c {{---}} центроид поддерева вершины v &amp;lt;/font &amp;gt;&lt;br /&gt;
     ch = children[c]&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{deleteEdges}&amp;lt;/tex&amp;gt;(c, children) &amp;lt;font color=green&amp;gt;// удаляем все ребра между вершиной с и детьми, чтобы мы не смогли из детей попасть в с. &lt;br /&gt;
                                                                                                                     Это полезно делать, если решение  подзадачи для поддерева предполагает проход dfs &amp;lt;/font &amp;gt;&lt;br /&gt;
     '''for''' c2 : ch[c]&lt;br /&gt;
          &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;(children, c2, sz)&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{mergeSolution}&amp;lt;/tex&amp;gt;(children, c2) &amp;lt;font color=green&amp;gt;// решаем для текущего поддерева &amp;lt;/font &amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Динамическая центроидная декомпозиция (дерево центроидной декомпозиции) ==&lt;br /&gt;
Теперь вернемся ко второй задаче введения. Для ее решения мы пользовались динамичекой версией divide &amp;amp; conquer {{---}} деревом отрезков. В предыдущем пункте мы определили статический оналог divide &amp;amp; conquer для дерева. Теперь обобщим этот метод для динамических задач.&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Деревом центроидов (или центроидной декомпозицией)''' дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; называют дерево &amp;lt;math&amp;gt;T(t)&amp;lt;/math&amp;gt;, построенное на вершинах дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; следующим образом :&lt;br /&gt;
* Пусть вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} центроид дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Объявим его корнем нового дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пусть при удалении вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; из дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; оно распалось на поддеревья &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, тогда детьми вершины c объявим &amp;lt;tex&amp;gt;T(t_1), T(t_t),\dots, T(t_k)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
=== Свойства центроидной декомпозиции ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
'''Свойства центроидной декомпозиции''' :&lt;br /&gt;
* Глубина дерева центроидов не превосходит &amp;lt;tex&amp;gt;log(n)&amp;lt;/tex&amp;gt;. &lt;br /&gt;
* Каждая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; является центроидом одного из поддеревьев дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;&lt;br /&gt;
* Каждая вершина дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; поддеревьям дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (еще говорят, что вершина принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; центроидам дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, или что эти центроиды содержат вершину)&lt;br /&gt;
* Для любых вершин &amp;lt;tex&amp;gt;u, v \in T (u \neq v)&amp;lt;/tex&amp;gt; верно ровно одно из следующих трех утверждений: &lt;br /&gt;
a) &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
b) &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
c) &amp;lt;tex&amp;gt;T(u) \cap T(v) = \emptyset &amp;lt;/tex&amp;gt;&lt;br /&gt;
* Простой путь между любой парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; содержит центроид &amp;lt;tex&amp;gt;c \in T(t)&amp;lt;/tex&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
* Действительно, т.к. размер поддерева &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; не превосходит &amp;lt;tex&amp;gt;\frac{|subtree(c)|}{2}&amp;lt;/tex&amp;gt;, то при спуске в каждую следующую вершину на пути к любому листу в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; размер поддерева вершины, в которой мы сейчас находимся, уменьшается как минимум на &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;. Значит длина всего пути до листа не превосходит &amp;lt;math&amp;gt;log(n)&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
* Второе свойство очевидно из построения дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, т.к. если вершина принадлежит дереву центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то она является центроидом, а из построения &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы знаем, что каждая вершина исходного дерева принадлежит и дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Третье свойство {{---}} прямое следствие первых двух, т.к. вершина принадлежит любому центроиду &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; т.и т.т., когда c {{---}} отец вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов. Т.к. вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; точно принадлежит дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (свойство 2), то она лежит на каком-то пути в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, причем все ее родители (центроиды) ее содержат. А по свойству 1 длина любого вертикального (и даже простого) пути есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
* Четвертое свойство очевидно из того, что &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; {{---}} дерево. Т.к. &amp;lt;math&amp;gt;T(u)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;T(v)&amp;lt;/math&amp;gt; {{---}} поддеревья различных вершин дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то либо они не пересекаются, либо &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;, либо &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
* Для доказательства последнего свойства выберем в качестве вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;lca(u, v)&amp;lt;/math&amp;gt; в дереве центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Покажем, что так выбранная вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удовлетворяет заявленным свойствам. То, что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt; {{---}} очевидно по определению &amp;lt;math&amp;gt;lca&amp;lt;/math&amp;gt;, т.к. каждый предок любой вершины в дереве центроидов содержит эту вершину. Теперь докажем, что &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежит на пути между парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt;. Т.к. &amp;lt;math&amp;gt;c = lca(u, v)&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то из &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; нет ребра в такого сына, который содержит одновременно &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в своем поддереве (в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;), значит после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; разделится на несколько поддеревьев, таких что вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; окажутся в разных компонентах связности. А значит найдется такое ребро &amp;lt;math&amp;gt;(c, x)&amp;lt;/math&amp;gt;, которое принадлежало пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, но после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удалилось. Это доказывает то, что вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежала на пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
С помощью описанных свойств дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы можем решить задачу 2 для дерева :&lt;br /&gt;
&lt;br /&gt;
=== Пример решения задачи с помощью центроидной декомпозиции === &lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин. В каждый момент времени любая вершина дерева может быть либо помечена, либо нет. Изначально помечена только вершина номер &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Дан список из &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; запросов: &lt;br /&gt;
* Вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; пометили.&lt;br /&gt;
* С вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; сняли пометку.&lt;br /&gt;
* Найти номер ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Решение :&lt;br /&gt;
&lt;br /&gt;
Построим центроидную декомпозицию &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Изначально посчитаем для каждого центроида, содержащего вершину &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt; посчитаем расстояние до вершины &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Для этого воспользуемся [[Метод_двоичного_подъема|методом двоичных подъемов для поиска lca пары вершин в дереве]], а также тем фактом, что если глубина &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве определена как расстояние от корня до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то длина пути между парой вершин &amp;lt;math&amp;gt;(u, v)&amp;lt;/math&amp;gt; есть &amp;lt;tex&amp;gt;len(u, v) = h(u) + h(v) - 2 \cdot h(lca(u, v))&amp;lt;/tex&amp;gt;. Если изначально предпосчитать проходом [[Обход_в_глубину,_цвета_вершин| dfs]] величины &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, то ответ на запрос &amp;lt;math&amp;gt;len(u, v)&amp;lt;/math&amp;gt; можно делать за время &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с &amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt;доп. памятью. Также можно воспользоваться техникой [[Сведение_задачи_LCA_к_задаче_RMQ|сведения задачи LCA к RMQ]] и решить с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; времени на запрос. Теперь научимся отвечать на запросы. Из последнего свойства центроидной декомпозиции видно, что если &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} искомая ближайшая помеченная вершина к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то путь между ними содержит центроид &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;, причем &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок одновременно вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве&amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Поэтому заведем [[Красно-черное_дерево|двоичное дерево поиска]] для каждого центроида &amp;lt;math&amp;gt;c \in T&amp;lt;/math&amp;gt;. В этой структуре для каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; будем хранить пары &amp;lt;math&amp;gt;(len(c, u), u)&amp;lt;/math&amp;gt; для всех помеченных вершин &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в поддереве центроидов &amp;lt;math&amp;gt;T(c)&amp;lt;/math&amp;gt;. Когда приходит запрос пометить вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} добавим в структуру данных для всех предков &amp;lt;math&amp;gt;p_c&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; пары &amp;lt;math&amp;gt;(len(p_c, v), v)&amp;lt;/math&amp;gt;. Мы совершим &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; добавлений, затратив &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; действий на каждое. Запрос снятия пометки с вершины обрабатывается аналогичными удалениями. Запрос поиска ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины {{---}} это запрос поиска вершины u, такой что  величина &amp;lt;math&amp;gt;len(c, u) + len(c, v)&amp;lt;/math&amp;gt; минимальна, где &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов (по пятому свойству, нас интересуют именно такие &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;). Этот запрос занимает так же &amp;lt;tex&amp;gt;O(log^2(n))&amp;lt;/tex&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
Итак, мы научились решать задачу с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и временной сложностью &amp;lt;math&amp;gt;O(log^2(n))&amp;lt;/math&amp;gt; на запрос любого типа с предварительным предпосчетом за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Варианты хранения центроидной декомпозиции ==&lt;br /&gt;
Для хранения дерева центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; есть &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; подхода, имеющих свои преимущества и недостатки :&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева запомним величину &amp;lt;math&amp;gt;p_v&amp;lt;/math&amp;gt; {{---}} номер предка вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Этот подход наиболее экономный по памяти (&amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;), но уступает в скорости и функциональности.&lt;br /&gt;
&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева будем хранить весь массив предков &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt; в дереве центроидов.&lt;br /&gt;
&lt;br /&gt;
Этот подход уступает в количестве необходимой дополнительной памяти (&amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt; суммарно), но имеет ряд преимуществ :&lt;br /&gt;
&lt;br /&gt;
1) При проходе по массиву предков фиксированной вершины будет выигрыш в скорости работы, т.к. весь массив будет лежать непрерывным блоком данных и следовательно будет закэширован&lt;br /&gt;
&lt;br /&gt;
2) На массиве предков можно строить различные структуры данных (такие как, например, дерево отрезков) для быстрого (в случае с деревом отрезков &amp;lt;math&amp;gt;O(log(log(n)))&amp;lt;/math&amp;gt; на запрос) поиска предка с необходимыми свойствами. Так, например, в описанной выше задаче про помеченные вершины наибольшего общего предка можно искать методом двоичных подъемов за &amp;lt;math&amp;gt;O(log(log(n))&amp;lt;/math&amp;gt; на запрос, т.к. размер массива предков есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; (по свойству 1 центроидной декомпозиции). Используя эту оптимизацию можно получить время &amp;lt;math&amp;gt;O(log(n)) + O(log(log(n)) = O(log(n))&amp;lt;/math&amp;gt; на запрос нахождения ближайшей помеченной вершины. Чтобы добиться улучшенной асимптотики для запросов изменения можно хранить дерево отрезков на каждом из путей &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt;, в каждой вершине которого хранить двоичное дерево поиска и поддерживать отложенные операции. Тогда ответ на эти запросы будет занимать &amp;lt;math&amp;gt;O(log(n) \cdot log(log(n))&amp;lt;/math&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
&lt;br /&gt;
*[[:Дерево_отрезков._Построение|Дерево отрезков]]&lt;br /&gt;
*[[:Heavy-light_декомпозиция|Heavy-light декомпозиция]]&lt;br /&gt;
*[[:Метод_двоичного_подъема| Метод двоичного подъема]]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы и структуры данных]]&lt;/div&gt;</summary>
		<author><name>92.255.113.52</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61625</id>
		<title>Centroid decomposition</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61625"/>
				<updated>2017-06-14T21:33:40Z</updated>
		
		<summary type="html">&lt;p&gt;92.255.113.52: /* Реализация */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Centroid decomposition (рус. центроидная декомпозиция) {{---}} это структура данных, позволяющая отвечать на запросы на дереве. Чаще всего это запросы, связанные с нахождением функции на вершинах, связанных неравенством на расстояние между ними в дереве. Также иногда применяется для запросов на путях в дереве.&lt;br /&gt;
&lt;br /&gt;
== Введение ==&lt;br /&gt;
Рассмотрим &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; задачи на обычном массиве (в дальнейшем мы будем их обобщать на случай дерева):&lt;br /&gt;
&lt;br /&gt;
Задача &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть массив &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; положительных целых чисел из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов и числа &amp;lt;tex&amp;gt;W \geqslant 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;рl&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; индексов массива, таких что &amp;lt;tex&amp;gt;|j - i| \leqslant l &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\sum_{i=0}^{n - 1} a_i \leqslant W&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
Задача &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть прямая дорога, на которой расположены &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; городов. В некоторых городах есть госпитали, которые могут принимать больных. Поступают запросы вида :&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;, в котором находится больной и требуется найти такой город &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;|u - v|&amp;lt;/tex&amp;gt; минимально возможное.&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что больше он не будет принимать больных&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что теперь он может принимать больных&lt;br /&gt;
}}&lt;br /&gt;
Для начала решим обе задачи. &lt;br /&gt;
Первая задача решается методом [[Сортировка_слиянием|divide &amp;amp; conquer (рус. разделяй и властвуй)]] {{---}} давайте разделим массив &amp;lt;tex&amp;gt;a[0 \dots n-1]&amp;lt;/tex&amp;gt; на 2 массива &amp;lt;tex&amp;gt;a[0\dots\frac{n}{2} - 1]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;a[\frac{n}{2} \dots n-1]&amp;lt;/tex&amp;gt; и рекурсивно решим задачу для каждого из них. Осталось научиться находить количество искомых пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt;, таких что &amp;lt;tex&amp;gt;i &amp;lt; \frac{n}{2}, j \geqslant \frac{n}{2}&amp;lt;/tex&amp;gt;. Для этого воспользуемся другой известной техникой {{---}} методом двух указателей. Посчитаем массив префиксных сумм для правой половины &amp;lt;tex&amp;gt;pref[i] = \sum_{j=\frac{n}{2}}^{i} a_j&amp;lt;/tex&amp;gt; и суффиксных (&amp;lt;tex&amp;gt;suf[i] = \sum_{j=i}^{\frac{n}{2} + 1} a_j&amp;lt;/tex&amp;gt;) {{---}} для левой. Заведем два указателя (&amp;lt;tex&amp;gt;p_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;p_2&amp;lt;/tex&amp;gt;). Изначально установим &amp;lt;tex&amp;gt;p_1 = \frac{n}{2} - l + 1, p_2 = \frac{n}{2}&amp;lt;/tex&amp;gt;. Пока &amp;lt;tex&amp;gt;p_2 - 1&amp;gt; \frac{n}{2}&amp;lt;/tex&amp;gt; и&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;pref[p_2] + suf[p_1] &amp;gt; W &amp;lt;/tex&amp;gt; будем уменьшать &amp;lt;math&amp;gt;p_2&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Если после этого &amp;lt;math&amp;gt;pref[p_2] + suf[p_1] \leqslant W&amp;lt;/math&amp;gt;, то к ответу прибавим &amp;lt;math&amp;gt;(p_2 - \frac{n}{2} + 1) * (\frac{n}{2} - p_1)&amp;lt;/math&amp;gt;, посго, увеличим &amp;lt;math&amp;gt;p_1&amp;lt;/math&amp;gt; на &amp;lt;math/math&amp;gt;. Так будем делать, пока &amp;lt;math&amp;gt;p_1 &amp;lt; \frac{n}{2}&amp;lt;/math&amp;gt;. В конце сложим текущий ответ и ответы для половин массива {{---}} получим ответ на задачу. Асимптотика такого алгоритма: &amp;lt;tex&amp;gt;T(n) = 2 * T(n / 2) + O(n) = O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вторая задача имеет запросы на изменение и поэтому надо применить динамическую версию divide &amp;amp; conquer {{---}} [[Дерево_отрезков._Построение|дерево отрезков]]. Построим дерево отрезков,&lt;br /&gt;
поддерживающее 2 вида запросов: присвоение в точке и минимум на отрезке. Изначально сделаем так, чтобы дереву отрезков соответствовал массив &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;b_i = 0&amp;lt;/tex&amp;gt;, если в i-м городе принимает госпиталь и &amp;lt;tex&amp;gt;b_i = 1&amp;lt;/tex&amp;gt; иначе. Когда в каком-то городе открывается/закрывается госпиталь {{---}} делаем запрос на изменение в дереве отрезков. Когда требуется узнать ближайщий госпиталь к &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-му городу, можно воспользоваться одной из следующих идей: &lt;br /&gt;
а) (&amp;lt;tex&amp;gt;O(n \cdot log^2(n)&amp;lt;/tex&amp;gt;) Бинарным поиском ищем ближайший слева и ближайший справа к i-му городу госпиталь (такой город &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, что &amp;lt;tex&amp;gt;\min\limits_{k=i..j}b_k= 1&amp;lt;/tex&amp;gt;). Для этого внутри бинарного поиска каждый раз делаем запрос на поиск минимума в дереве отрезков.  &lt;br /&gt;
б) (&amp;lt;tex&amp;gt;O(n \cdot log(n)&amp;lt;/tex&amp;gt;) Будем одним спуском/подъемом по дереву определять, куда нам нужно идти (в левое или правое поддерево), тем самым делая одновоременно и бинарный поиск, и спуск/подъем по дереву.&lt;br /&gt;
&lt;br /&gt;
== Статическая центроидная декомпозиция ==&lt;br /&gt;
Перейдем к обобщению поставленных задач на случай дерева. Начнем, как и полагается, с первой:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть взвешенное дерево &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин, в каждой вершине которого написаны положительные целые числа. Также по-прежнему даны числа &amp;lt;tex&amp;gt;W &amp;gt;= 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; вершин дерева, таких что расстояние между ними не превосходит &amp;lt;math&amp;gt;l&amp;lt;/math&amp;gt; по числу ребер и не превосходит &amp;lt;math&amp;gt;W&amp;lt;/math&amp;gt; по сумме весов.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Для решения новой задачи применим ту же идею, что и была до этого {{---}} разделяй и властвуй. Для этого нам потребуется следующий объект :&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|id=191213&lt;br /&gt;
|definition =&lt;br /&gt;
'''Центроидом дерева''' (англ. ''centroid'') называется такая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, после удаления которой дерево разбивается на несколько (&amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;) поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, таких что для каждого &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;: &amp;lt;tex&amp;gt;|t_i| \leqslant \frac{n}{2}&amp;lt;/tex&amp;gt;, т.е. размер каждого поддерева не превосходит половины размера исходного дерева.&lt;br /&gt;
}}&lt;br /&gt;
Итак, в случае дерева идея разделяй-и-властвуй из предыдущего пункта будет формулироваться так: найдем центроид (доказательство её существования и алгоритм нахождение см. далее). Предположим, что мы сумели найти центроид за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; {{---}} размер дерева. Тогда, как и в упрощенной версии задачи {{---}} рекурсивно найдем ответ для всех поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, после чего попытаемся найти недостающие пары вершин, находящиехя в разных поддеревьях и удовлетворяющих вопросу задачи. Для этого будем отвечать на следующие запросы: пусть мы сейчас считаем все пары, где первая из вершин находится в поддереве &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и мы в некоторой структуре данных &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; храним все вершины остальных деревьев (каждую вершину задаем парой &amp;lt;math&amp;gt;(depth(v), length(v))&amp;lt;/math&amp;gt; {{---}} глубина вершины и длина пути до нее из корня поддерева), расстояние до которых от корня их поддерева не превышает &amp;lt;math&amp;gt;min(l, n)&amp;lt;/math&amp;gt;. Тогда просто пройдемся по всем вершинам &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; поддерева &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и прибавим к ответу число вершин в структуре &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt;, таких, что &amp;lt;tex&amp;gt;depth(u) \leqslant l - depth(v)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;length(u) \leqslant l - length(v)&amp;lt;/tex&amp;gt;. Это двумерные запросы, на которые можно отвечать за &amp;lt;math&amp;gt;O(log^2(n)&amp;lt;/math&amp;gt; с помощью [[Многомерное_дерево_отрезков|2d-дерева отрезков]], либо за &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с помощью [[Перечисление_точек_в_произвольном_прямоугольнике_за_n_*_log_%5E(d_-_1)_n_(range_tree)|техники поиска точек в d-мерном пространстве]]. Также читателю предлагается придумать и более эффективные и простые способы решить эту подзадачу.&lt;br /&gt;
&lt;br /&gt;
Оценим итоговую асимптотику: &amp;lt;tex&amp;gt;T(n) = k * T(n / k) + O(n \cdot log(n))&amp;lt;/tex&amp;gt;. Решая это рекурентное соотношение, получим &amp;lt;math&amp;gt;O(n \cdot log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь, как и было обещено, докажем лемму о существовании центроида и опишем алгоритм его эффективного поиска.&lt;br /&gt;
&lt;br /&gt;
=== Лемма о существовании центроида и алгоритм его нахождения. ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
В любом дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; существует центроид.&lt;br /&gt;
|proof=&lt;br /&gt;
Рассмотрим корень дерева &amp;lt;math&amp;gt;(r)&amp;lt;/math&amp;gt;. Положим изначально &amp;lt;math&amp;gt;v = r&amp;lt;/math&amp;gt;. Изначально &amp;lt;math&amp;gt;|subtree(v)| = n&amp;lt;/math&amp;gt;. Среди всех детей &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; выберем вершину &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; с максимальным размером поддерева. Если &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид, то положим &amp;lt;math&amp;gt;v = u&amp;lt;/math&amp;gt; и продолжим выбор нового u, иначе {{---}} остановимся. Докажем, что мы в какой-то момент остановимся. Пусть в призвольный момент времени &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид и размер её наддерева меньше &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, значит максимальное поддерево имеет размер больше чем &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, т.е. &amp;lt;math&amp;gt;|subtree(u)| &amp;gt; \frac{n}{2}&amp;lt;/math&amp;gt;, а значит размер &amp;quot;наддерева&amp;quot; вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; равен &amp;lt;tex&amp;gt;n - |subtree(u)| &amp;lt; \frac{n}{2}&amp;lt;/tex&amp;gt;. При этом теперь размер любого поддерева, на которое распадется дерево t при удалении вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; не превосходит &amp;lt;math&amp;gt;|subtree(u)| - 1&amp;lt;/math&amp;gt;, т.к. наддерево имеет размер меньше, чем поддерево &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, а любое поддерево вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; имеет хотя бы на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; вершину меньше (сама вершина &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;). По индукции получаем, что в любой момент времени размер наддерева вершины v меньше &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, значит мы будем спускаться только вниз по дереву &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, и при переходе к вершине &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} сыну &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; размер максимального поддерева уменьшится как минимум на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Значит не более чем за &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; шагов наши действия прекратятся и мы окажемся в центроиде дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
Итак, мы конструктивно доказали существование центроида и привели линейный относительно размера дерева алгоритм его нахождения. &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Реализация ===&lt;br /&gt;
&lt;br /&gt;
Поиск центроида в дереве:&lt;br /&gt;
&lt;br /&gt;
  '''int''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int''' sz[n])&lt;br /&gt;
     max_subtree = -1&lt;br /&gt;
     '''for''' u : children(v)&lt;br /&gt;
          '''if''' sz[u] &amp;gt; sz[v] / 2 and sz[u] &amp;gt; sz[max_subtree] &amp;lt;font color=green&amp;gt;// считаем sz[-1] = 0 &amp;lt;/font &amp;gt;&lt;br /&gt;
               max_subtree = u&lt;br /&gt;
     '''if''' max_subtree == -1&lt;br /&gt;
          return v&lt;br /&gt;
     '''else''' &lt;br /&gt;
          '''return''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz)&lt;br /&gt;
&lt;br /&gt;
Шаблон решения произвольной задачи на статическую центроидную декомпозицию:&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int''' sz[n])&lt;br /&gt;
     c = &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz) &amp;lt;font color=green&amp;gt;// находим c {{---}} центроид поддерева вершины v &amp;lt;/font &amp;gt;&lt;br /&gt;
     ch = children[c]&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{deleteEdges}&amp;lt;/tex&amp;gt;(c, children) &amp;lt;font color=green&amp;gt;// удаляем все ребра между вершиной с и детьми, чтобы мы не смогли из детей попасть в с. &lt;br /&gt;
Это полезно делать, если решение  подзадачи для поддерева предполагает проход dfs &amp;lt;/font &amp;gt;&lt;br /&gt;
     '''for''' c2 : ch[c]&lt;br /&gt;
          &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;(children, c2, sz)&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{mergeSolution}&amp;lt;/tex&amp;gt;(children, c2) &amp;lt;font color=green&amp;gt;// решаем для текущего поддерева &amp;lt;/font &amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Динамическая центроидная декомпозиция (дерево центроидной декомпозиции) ==&lt;br /&gt;
Теперь вернемся ко второй задаче введения. Для ее решения мы пользовались динамичекой версией divide &amp;amp; conquer {{---}} деревом отрезков. В предыдущем пункте мы определили статический оналог divide &amp;amp; conquer для дерева. Теперь обобщим этот метод для динамических задач.&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Деревом центроидов (или центроидной декомпозицией)''' дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; называют дерево &amp;lt;math&amp;gt;T(t)&amp;lt;/math&amp;gt;, построенное на вершинах дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; следующим образом :&lt;br /&gt;
* Пусть вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} центроид дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Объявим его корнем нового дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пусть при удалении вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; из дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; оно распалось на поддеревья &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, тогда детьми вершины c объявим &amp;lt;tex&amp;gt;T(t_1), T(t_t),\dots, T(t_k)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
=== Свойства центроидной декомпозиции ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
'''Свойства центроидной декомпозиции''' :&lt;br /&gt;
* Глубина дерева центроидов не превосходит &amp;lt;tex&amp;gt;log(n)&amp;lt;/tex&amp;gt;. &lt;br /&gt;
* Каждая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; является центроидом одного из поддеревьев дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;&lt;br /&gt;
* Каждая вершина дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; поддеревьям дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (еще говорят, что вершина принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; центроидам дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, или что эти центроиды содержат вершину)&lt;br /&gt;
* Для любых вершин &amp;lt;tex&amp;gt;u, v \in T (u \neq v)&amp;lt;/tex&amp;gt; верно ровно одно из следующих трех утверждений: &lt;br /&gt;
a) &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
b) &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
c) &amp;lt;tex&amp;gt;T(u) \cap T(v) = \emptyset &amp;lt;/tex&amp;gt;&lt;br /&gt;
* Простой путь между любой парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; содержит центроид &amp;lt;tex&amp;gt;c \in T(t)&amp;lt;/tex&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
* Действительно, т.к. размер поддерева &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; не превосходит &amp;lt;tex&amp;gt;\frac{|subtree(c)|}{2}&amp;lt;/tex&amp;gt;, то при спуске в каждую следующую вершину на пути к любому листу в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; размер поддерева вершины, в которой мы сейчас находимся, уменьшается как минимум на &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;. Значит длина всего пути до листа не превосходит &amp;lt;math&amp;gt;log(n)&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
* Второе свойство очевидно из построения дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, т.к. если вершина принадлежит дереву центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то она является центроидом, а из построения &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы знаем, что каждая вершина исходного дерева принадлежит и дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Третье свойство {{---}} прямое следствие первых двух, т.к. вершина принадлежит любому центроиду &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; т.и т.т., когда c {{---}} отец вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов. Т.к. вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; точно принадлежит дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (свойство 2), то она лежит на каком-то пути в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, причем все ее родители (центроиды) ее содержат. А по свойству 1 длина любого вертикального (и даже простого) пути есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
* Четвертое свойство очевидно из того, что &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; {{---}} дерево. Т.к. &amp;lt;math&amp;gt;T(u)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;T(v)&amp;lt;/math&amp;gt; {{---}} поддеревья различных вершин дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то либо они не пересекаются, либо &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;, либо &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
* Для доказательства последнего свойства выберем в качестве вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;lca(u, v)&amp;lt;/math&amp;gt; в дереве центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Покажем, что так выбранная вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удовлетворяет заявленным свойствам. То, что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt; {{---}} очевидно по определению &amp;lt;math&amp;gt;lca&amp;lt;/math&amp;gt;, т.к. каждый предок любой вершины в дереве центроидов содержит эту вершину. Теперь докажем, что &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежит на пути между парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt;. Т.к. &amp;lt;math&amp;gt;c = lca(u, v)&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то из &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; нет ребра в такого сына, который содержит одновременно &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в своем поддереве (в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;), значит после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; разделится на несколько поддеревьев, таких что вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; окажутся в разных компонентах связности. А значит найдется такое ребро &amp;lt;math&amp;gt;(c, x)&amp;lt;/math&amp;gt;, которое принадлежало пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, но после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удалилось. Это доказывает то, что вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежала на пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
С помощью описанных свойств дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы можем решить задачу 2 для дерева :&lt;br /&gt;
&lt;br /&gt;
=== Пример решения задачи с помощью центроидной декомпозиции === &lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин. В каждый момент времени любая вершина дерева может быть либо помечена, либо нет. Изначально помечена только вершина номер &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Дан список из &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; запросов: &lt;br /&gt;
* Вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; пометили.&lt;br /&gt;
* С вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; сняли пометку.&lt;br /&gt;
* Найти номер ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Решение :&lt;br /&gt;
&lt;br /&gt;
Построим центроидную декомпозицию &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Изначально посчитаем для каждого центроида, содержащего вершину &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt; посчитаем расстояние до вершины &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Для этого воспользуемся [[Метод_двоичного_подъема|методом двоичных подъемов для поиска lca пары вершин в дереве]], а также тем фактом, что если глубина &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве определена как расстояние от корня до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то длина пути между парой вершин &amp;lt;math&amp;gt;(u, v)&amp;lt;/math&amp;gt; есть &amp;lt;tex&amp;gt;len(u, v) = h(u) + h(v) - 2 \cdot h(lca(u, v))&amp;lt;/tex&amp;gt;. Если изначально предпосчитать проходом [[Обход_в_глубину,_цвета_вершин| dfs]] величины &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, то ответ на запрос &amp;lt;math&amp;gt;len(u, v)&amp;lt;/math&amp;gt; можно делать за время &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с &amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt;доп. памятью. Также можно воспользоваться техникой [[Сведение_задачи_LCA_к_задаче_RMQ|сведения задачи LCA к RMQ]] и решить с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; времени на запрос. Теперь научимся отвечать на запросы. Из последнего свойства центроидной декомпозиции видно, что если &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} искомая ближайшая помеченная вершина к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то путь между ними содержит центроид &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;, причем &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок одновременно вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве&amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Поэтому заведем [[Красно-черное_дерево|двоичное дерево поиска]] для каждого центроида &amp;lt;math&amp;gt;c \in T&amp;lt;/math&amp;gt;. В этой структуре для каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; будем хранить пары &amp;lt;math&amp;gt;(len(c, u), u)&amp;lt;/math&amp;gt; для всех помеченных вершин &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в поддереве центроидов &amp;lt;math&amp;gt;T(c)&amp;lt;/math&amp;gt;. Когда приходит запрос пометить вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} добавим в структуру данных для всех предков &amp;lt;math&amp;gt;p_c&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; пары &amp;lt;math&amp;gt;(len(p_c, v), v)&amp;lt;/math&amp;gt;. Мы совершим &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; добавлений, затратив &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; действий на каждое. Запрос снятия пометки с вершины обрабатывается аналогичными удалениями. Запрос поиска ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины {{---}} это запрос поиска вершины u, такой что  величина &amp;lt;math&amp;gt;len(c, u) + len(c, v)&amp;lt;/math&amp;gt; минимальна, где &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов (по пятому свойству, нас интересуют именно такие &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;). Этот запрос занимает так же &amp;lt;tex&amp;gt;O(log^2(n))&amp;lt;/tex&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
Итак, мы научились решать задачу с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и временной сложностью &amp;lt;math&amp;gt;O(log^2(n))&amp;lt;/math&amp;gt; на запрос любого типа с предварительным предпосчетом за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Варианты хранения центроидной декомпозиции ==&lt;br /&gt;
Для хранения дерева центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; есть &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; подхода, имеющих свои преимущества и недостатки :&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева запомним величину &amp;lt;math&amp;gt;p_v&amp;lt;/math&amp;gt; {{---}} номер предка вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Этот подход наиболее экономный по памяти (&amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;), но уступает в скорости и функциональности.&lt;br /&gt;
&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева будем хранить весь массив предков &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt; в дереве центроидов.&lt;br /&gt;
&lt;br /&gt;
Этот подход уступает в количестве необходимой дополнительной памяти (&amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt; суммарно), но имеет ряд преимуществ :&lt;br /&gt;
&lt;br /&gt;
1) При проходе по массиву предков фиксированной вершины будет выигрыш в скорости работы, т.к. весь массив будет лежать непрерывным блоком данных и следовательно будет закэширован&lt;br /&gt;
&lt;br /&gt;
2) На массиве предков можно строить различные структуры данных (такие как, например, дерево отрезков) для быстрого (в случае с деревом отрезков &amp;lt;math&amp;gt;O(log(log(n)))&amp;lt;/math&amp;gt; на запрос) поиска предка с необходимыми свойствами. Так, например, в описанной выше задаче про помеченные вершины наибольшего общего предка можно искать методом двоичных подъемов за &amp;lt;math&amp;gt;O(log(log(n))&amp;lt;/math&amp;gt; на запрос, т.к. размер массива предков есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; (по свойству 1 центроидной декомпозиции). Используя эту оптимизацию можно получить время &amp;lt;math&amp;gt;O(log(n)) + O(log(log(n)) = O(log(n))&amp;lt;/math&amp;gt; на запрос нахождения ближайшей помеченной вершины. Чтобы добиться улучшенной асимптотики для запросов изменения можно хранить дерево отрезков на каждом из путей &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt;, в каждой вершине которого хранить двоичное дерево поиска и поддерживать отложенные операции. Тогда ответ на эти запросы будет занимать &amp;lt;math&amp;gt;O(log(n) \cdot log(log(n))&amp;lt;/math&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
&lt;br /&gt;
*[[:Дерево_отрезков._Построение|Дерево отрезков]]&lt;br /&gt;
*[[:Heavy-light_декомпозиция|Heavy-light декомпозиция]]&lt;br /&gt;
*[[:Метод_двоичного_подъема| Метод двоичного подъема]]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы и структуры данных]]&lt;/div&gt;</summary>
		<author><name>92.255.113.52</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61624</id>
		<title>Centroid decomposition</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61624"/>
				<updated>2017-06-14T21:33:09Z</updated>
		
		<summary type="html">&lt;p&gt;92.255.113.52: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Centroid decomposition (рус. центроидная декомпозиция) {{---}} это структура данных, позволяющая отвечать на запросы на дереве. Чаще всего это запросы, связанные с нахождением функции на вершинах, связанных неравенством на расстояние между ними в дереве. Также иногда применяется для запросов на путях в дереве.&lt;br /&gt;
&lt;br /&gt;
== Введение ==&lt;br /&gt;
Рассмотрим &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; задачи на обычном массиве (в дальнейшем мы будем их обобщать на случай дерева):&lt;br /&gt;
&lt;br /&gt;
Задача &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть массив &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; положительных целых чисел из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов и числа &amp;lt;tex&amp;gt;W \geqslant 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;рl&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; индексов массива, таких что &amp;lt;tex&amp;gt;|j - i| \leqslant l &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\sum_{i=0}^{n - 1} a_i \leqslant W&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
Задача &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть прямая дорога, на которой расположены &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; городов. В некоторых городах есть госпитали, которые могут принимать больных. Поступают запросы вида :&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;, в котором находится больной и требуется найти такой город &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;|u - v|&amp;lt;/tex&amp;gt; минимально возможное.&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что больше он не будет принимать больных&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что теперь он может принимать больных&lt;br /&gt;
}}&lt;br /&gt;
Для начала решим обе задачи. &lt;br /&gt;
Первая задача решается методом [[Сортировка_слиянием|divide &amp;amp; conquer (рус. разделяй и властвуй)]] {{---}} давайте разделим массив &amp;lt;tex&amp;gt;a[0 \dots n-1]&amp;lt;/tex&amp;gt; на 2 массива &amp;lt;tex&amp;gt;a[0\dots\frac{n}{2} - 1]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;a[\frac{n}{2} \dots n-1]&amp;lt;/tex&amp;gt; и рекурсивно решим задачу для каждого из них. Осталось научиться находить количество искомых пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt;, таких что &amp;lt;tex&amp;gt;i &amp;lt; \frac{n}{2}, j \geqslant \frac{n}{2}&amp;lt;/tex&amp;gt;. Для этого воспользуемся другой известной техникой {{---}} методом двух указателей. Посчитаем массив префиксных сумм для правой половины &amp;lt;tex&amp;gt;pref[i] = \sum_{j=\frac{n}{2}}^{i} a_j&amp;lt;/tex&amp;gt; и суффиксных (&amp;lt;tex&amp;gt;suf[i] = \sum_{j=i}^{\frac{n}{2} + 1} a_j&amp;lt;/tex&amp;gt;) {{---}} для левой. Заведем два указателя (&amp;lt;tex&amp;gt;p_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;p_2&amp;lt;/tex&amp;gt;). Изначально установим &amp;lt;tex&amp;gt;p_1 = \frac{n}{2} - l + 1, p_2 = \frac{n}{2}&amp;lt;/tex&amp;gt;. Пока &amp;lt;tex&amp;gt;p_2 - 1&amp;gt; \frac{n}{2}&amp;lt;/tex&amp;gt; и&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;pref[p_2] + suf[p_1] &amp;gt; W &amp;lt;/tex&amp;gt; будем уменьшать &amp;lt;math&amp;gt;p_2&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Если после этого &amp;lt;math&amp;gt;pref[p_2] + suf[p_1] \leqslant W&amp;lt;/math&amp;gt;, то к ответу прибавим &amp;lt;math&amp;gt;(p_2 - \frac{n}{2} + 1) * (\frac{n}{2} - p_1)&amp;lt;/math&amp;gt;, посго, увеличим &amp;lt;math&amp;gt;p_1&amp;lt;/math&amp;gt; на &amp;lt;math/math&amp;gt;. Так будем делать, пока &amp;lt;math&amp;gt;p_1 &amp;lt; \frac{n}{2}&amp;lt;/math&amp;gt;. В конце сложим текущий ответ и ответы для половин массива {{---}} получим ответ на задачу. Асимптотика такого алгоритма: &amp;lt;tex&amp;gt;T(n) = 2 * T(n / 2) + O(n) = O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вторая задача имеет запросы на изменение и поэтому надо применить динамическую версию divide &amp;amp; conquer {{---}} [[Дерево_отрезков._Построение|дерево отрезков]]. Построим дерево отрезков,&lt;br /&gt;
поддерживающее 2 вида запросов: присвоение в точке и минимум на отрезке. Изначально сделаем так, чтобы дереву отрезков соответствовал массив &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;b_i = 0&amp;lt;/tex&amp;gt;, если в i-м городе принимает госпиталь и &amp;lt;tex&amp;gt;b_i = 1&amp;lt;/tex&amp;gt; иначе. Когда в каком-то городе открывается/закрывается госпиталь {{---}} делаем запрос на изменение в дереве отрезков. Когда требуется узнать ближайщий госпиталь к &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-му городу, можно воспользоваться одной из следующих идей: &lt;br /&gt;
а) (&amp;lt;tex&amp;gt;O(n \cdot log^2(n)&amp;lt;/tex&amp;gt;) Бинарным поиском ищем ближайший слева и ближайший справа к i-му городу госпиталь (такой город &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, что &amp;lt;tex&amp;gt;\min\limits_{k=i..j}b_k= 1&amp;lt;/tex&amp;gt;). Для этого внутри бинарного поиска каждый раз делаем запрос на поиск минимума в дереве отрезков.  &lt;br /&gt;
б) (&amp;lt;tex&amp;gt;O(n \cdot log(n)&amp;lt;/tex&amp;gt;) Будем одним спуском/подъемом по дереву определять, куда нам нужно идти (в левое или правое поддерево), тем самым делая одновоременно и бинарный поиск, и спуск/подъем по дереву.&lt;br /&gt;
&lt;br /&gt;
== Статическая центроидная декомпозиция ==&lt;br /&gt;
Перейдем к обобщению поставленных задач на случай дерева. Начнем, как и полагается, с первой:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть взвешенное дерево &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин, в каждой вершине которого написаны положительные целые числа. Также по-прежнему даны числа &amp;lt;tex&amp;gt;W &amp;gt;= 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; вершин дерева, таких что расстояние между ними не превосходит &amp;lt;math&amp;gt;l&amp;lt;/math&amp;gt; по числу ребер и не превосходит &amp;lt;math&amp;gt;W&amp;lt;/math&amp;gt; по сумме весов.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Для решения новой задачи применим ту же идею, что и была до этого {{---}} разделяй и властвуй. Для этого нам потребуется следующий объект :&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|id=191213&lt;br /&gt;
|definition =&lt;br /&gt;
'''Центроидом дерева''' (англ. ''centroid'') называется такая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, после удаления которой дерево разбивается на несколько (&amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;) поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, таких что для каждого &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;: &amp;lt;tex&amp;gt;|t_i| \leqslant \frac{n}{2}&amp;lt;/tex&amp;gt;, т.е. размер каждого поддерева не превосходит половины размера исходного дерева.&lt;br /&gt;
}}&lt;br /&gt;
Итак, в случае дерева идея разделяй-и-властвуй из предыдущего пункта будет формулироваться так: найдем центроид (доказательство её существования и алгоритм нахождение см. далее). Предположим, что мы сумели найти центроид за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; {{---}} размер дерева. Тогда, как и в упрощенной версии задачи {{---}} рекурсивно найдем ответ для всех поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, после чего попытаемся найти недостающие пары вершин, находящиехя в разных поддеревьях и удовлетворяющих вопросу задачи. Для этого будем отвечать на следующие запросы: пусть мы сейчас считаем все пары, где первая из вершин находится в поддереве &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и мы в некоторой структуре данных &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; храним все вершины остальных деревьев (каждую вершину задаем парой &amp;lt;math&amp;gt;(depth(v), length(v))&amp;lt;/math&amp;gt; {{---}} глубина вершины и длина пути до нее из корня поддерева), расстояние до которых от корня их поддерева не превышает &amp;lt;math&amp;gt;min(l, n)&amp;lt;/math&amp;gt;. Тогда просто пройдемся по всем вершинам &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; поддерева &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и прибавим к ответу число вершин в структуре &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt;, таких, что &amp;lt;tex&amp;gt;depth(u) \leqslant l - depth(v)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;length(u) \leqslant l - length(v)&amp;lt;/tex&amp;gt;. Это двумерные запросы, на которые можно отвечать за &amp;lt;math&amp;gt;O(log^2(n)&amp;lt;/math&amp;gt; с помощью [[Многомерное_дерево_отрезков|2d-дерева отрезков]], либо за &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с помощью [[Перечисление_точек_в_произвольном_прямоугольнике_за_n_*_log_%5E(d_-_1)_n_(range_tree)|техники поиска точек в d-мерном пространстве]]. Также читателю предлагается придумать и более эффективные и простые способы решить эту подзадачу.&lt;br /&gt;
&lt;br /&gt;
Оценим итоговую асимптотику: &amp;lt;tex&amp;gt;T(n) = k * T(n / k) + O(n \cdot log(n))&amp;lt;/tex&amp;gt;. Решая это рекурентное соотношение, получим &amp;lt;math&amp;gt;O(n \cdot log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь, как и было обещено, докажем лемму о существовании центроида и опишем алгоритм его эффективного поиска.&lt;br /&gt;
&lt;br /&gt;
=== Лемма о существовании центроида и алгоритм его нахождения. ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
В любом дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; существует центроид.&lt;br /&gt;
|proof=&lt;br /&gt;
Рассмотрим корень дерева &amp;lt;math&amp;gt;(r)&amp;lt;/math&amp;gt;. Положим изначально &amp;lt;math&amp;gt;v = r&amp;lt;/math&amp;gt;. Изначально &amp;lt;math&amp;gt;|subtree(v)| = n&amp;lt;/math&amp;gt;. Среди всех детей &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; выберем вершину &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; с максимальным размером поддерева. Если &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид, то положим &amp;lt;math&amp;gt;v = u&amp;lt;/math&amp;gt; и продолжим выбор нового u, иначе {{---}} остановимся. Докажем, что мы в какой-то момент остановимся. Пусть в призвольный момент времени &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид и размер её наддерева меньше &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, значит максимальное поддерево имеет размер больше чем &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, т.е. &amp;lt;math&amp;gt;|subtree(u)| &amp;gt; \frac{n}{2}&amp;lt;/math&amp;gt;, а значит размер &amp;quot;наддерева&amp;quot; вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; равен &amp;lt;tex&amp;gt;n - |subtree(u)| &amp;lt; \frac{n}{2}&amp;lt;/tex&amp;gt;. При этом теперь размер любого поддерева, на которое распадется дерево t при удалении вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; не превосходит &amp;lt;math&amp;gt;|subtree(u)| - 1&amp;lt;/math&amp;gt;, т.к. наддерево имеет размер меньше, чем поддерево &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, а любое поддерево вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; имеет хотя бы на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; вершину меньше (сама вершина &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;). По индукции получаем, что в любой момент времени размер наддерева вершины v меньше &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, значит мы будем спускаться только вниз по дереву &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, и при переходе к вершине &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} сыну &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; размер максимального поддерева уменьшится как минимум на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Значит не более чем за &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; шагов наши действия прекратятся и мы окажемся в центроиде дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
Итак, мы конструктивно доказали существование центроида и привели линейный относительно размера дерева алгоритм его нахождения. &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Реализация ===&lt;br /&gt;
&lt;br /&gt;
Поиск центроида в дереве:&lt;br /&gt;
&lt;br /&gt;
  '''int''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int''' sz[n])&lt;br /&gt;
     max_subtree = -1&lt;br /&gt;
     '''for''' u : children(v)&lt;br /&gt;
          '''if''' sz[u] &amp;gt; sz[v] / 2 and sz[u] &amp;gt; sz[max_subtree] &amp;lt;font color=green&amp;gt;// считаем sz[-1] = 0 &amp;lt;/font &amp;gt;&lt;br /&gt;
               max_subtree = u&lt;br /&gt;
     '''if''' max_subtree == -1&lt;br /&gt;
          return v&lt;br /&gt;
     '''else''' &lt;br /&gt;
          '''return''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz)&lt;br /&gt;
&lt;br /&gt;
Шаблон решения произвольной задачи на статическую центроидную декомпозицию:&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int''' sz[n])&lt;br /&gt;
     c = &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz) &amp;lt;font color=green&amp;gt;// находим c {{---}} центроид поддерева вершины v &amp;lt;/font &amp;gt;&lt;br /&gt;
     ch = children[c]&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{deleteEdges}&amp;lt;/tex&amp;gt;(c, children) &amp;lt;font color=green&amp;gt;// удаляем все ребра между вершиной с и детьми, чтобы мы не смогли из детей попасть в с. Это полезно делать, если решение  подзадачи для поддерева предполагает проход dfs &amp;lt;/font &amp;gt;&lt;br /&gt;
     '''for''' c2 : ch[c]&lt;br /&gt;
          &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;(children, c2, sz)&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{mergeSolution}&amp;lt;/tex&amp;gt;(children, c2) &amp;lt;font color=green&amp;gt;// решаем для текущего поддерева &amp;lt;/font &amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Динамическая центроидная декомпозиция (дерево центроидной декомпозиции) ==&lt;br /&gt;
Теперь вернемся ко второй задаче введения. Для ее решения мы пользовались динамичекой версией divide &amp;amp; conquer {{---}} деревом отрезков. В предыдущем пункте мы определили статический оналог divide &amp;amp; conquer для дерева. Теперь обобщим этот метод для динамических задач.&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Деревом центроидов (или центроидной декомпозицией)''' дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; называют дерево &amp;lt;math&amp;gt;T(t)&amp;lt;/math&amp;gt;, построенное на вершинах дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; следующим образом :&lt;br /&gt;
* Пусть вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} центроид дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Объявим его корнем нового дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пусть при удалении вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; из дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; оно распалось на поддеревья &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, тогда детьми вершины c объявим &amp;lt;tex&amp;gt;T(t_1), T(t_t),\dots, T(t_k)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
=== Свойства центроидной декомпозиции ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
'''Свойства центроидной декомпозиции''' :&lt;br /&gt;
* Глубина дерева центроидов не превосходит &amp;lt;tex&amp;gt;log(n)&amp;lt;/tex&amp;gt;. &lt;br /&gt;
* Каждая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; является центроидом одного из поддеревьев дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;&lt;br /&gt;
* Каждая вершина дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; поддеревьям дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (еще говорят, что вершина принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; центроидам дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, или что эти центроиды содержат вершину)&lt;br /&gt;
* Для любых вершин &amp;lt;tex&amp;gt;u, v \in T (u \neq v)&amp;lt;/tex&amp;gt; верно ровно одно из следующих трех утверждений: &lt;br /&gt;
a) &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
b) &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
c) &amp;lt;tex&amp;gt;T(u) \cap T(v) = \emptyset &amp;lt;/tex&amp;gt;&lt;br /&gt;
* Простой путь между любой парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; содержит центроид &amp;lt;tex&amp;gt;c \in T(t)&amp;lt;/tex&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
* Действительно, т.к. размер поддерева &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; не превосходит &amp;lt;tex&amp;gt;\frac{|subtree(c)|}{2}&amp;lt;/tex&amp;gt;, то при спуске в каждую следующую вершину на пути к любому листу в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; размер поддерева вершины, в которой мы сейчас находимся, уменьшается как минимум на &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;. Значит длина всего пути до листа не превосходит &amp;lt;math&amp;gt;log(n)&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
* Второе свойство очевидно из построения дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, т.к. если вершина принадлежит дереву центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то она является центроидом, а из построения &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы знаем, что каждая вершина исходного дерева принадлежит и дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Третье свойство {{---}} прямое следствие первых двух, т.к. вершина принадлежит любому центроиду &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; т.и т.т., когда c {{---}} отец вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов. Т.к. вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; точно принадлежит дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (свойство 2), то она лежит на каком-то пути в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, причем все ее родители (центроиды) ее содержат. А по свойству 1 длина любого вертикального (и даже простого) пути есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
* Четвертое свойство очевидно из того, что &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; {{---}} дерево. Т.к. &amp;lt;math&amp;gt;T(u)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;T(v)&amp;lt;/math&amp;gt; {{---}} поддеревья различных вершин дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то либо они не пересекаются, либо &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;, либо &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
* Для доказательства последнего свойства выберем в качестве вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;lca(u, v)&amp;lt;/math&amp;gt; в дереве центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Покажем, что так выбранная вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удовлетворяет заявленным свойствам. То, что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt; {{---}} очевидно по определению &amp;lt;math&amp;gt;lca&amp;lt;/math&amp;gt;, т.к. каждый предок любой вершины в дереве центроидов содержит эту вершину. Теперь докажем, что &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежит на пути между парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt;. Т.к. &amp;lt;math&amp;gt;c = lca(u, v)&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то из &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; нет ребра в такого сына, который содержит одновременно &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в своем поддереве (в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;), значит после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; разделится на несколько поддеревьев, таких что вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; окажутся в разных компонентах связности. А значит найдется такое ребро &amp;lt;math&amp;gt;(c, x)&amp;lt;/math&amp;gt;, которое принадлежало пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, но после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удалилось. Это доказывает то, что вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежала на пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
С помощью описанных свойств дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы можем решить задачу 2 для дерева :&lt;br /&gt;
&lt;br /&gt;
=== Пример решения задачи с помощью центроидной декомпозиции === &lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин. В каждый момент времени любая вершина дерева может быть либо помечена, либо нет. Изначально помечена только вершина номер &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Дан список из &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; запросов: &lt;br /&gt;
* Вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; пометили.&lt;br /&gt;
* С вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; сняли пометку.&lt;br /&gt;
* Найти номер ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Решение :&lt;br /&gt;
&lt;br /&gt;
Построим центроидную декомпозицию &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Изначально посчитаем для каждого центроида, содержащего вершину &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt; посчитаем расстояние до вершины &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Для этого воспользуемся [[Метод_двоичного_подъема|методом двоичных подъемов для поиска lca пары вершин в дереве]], а также тем фактом, что если глубина &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве определена как расстояние от корня до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то длина пути между парой вершин &amp;lt;math&amp;gt;(u, v)&amp;lt;/math&amp;gt; есть &amp;lt;tex&amp;gt;len(u, v) = h(u) + h(v) - 2 \cdot h(lca(u, v))&amp;lt;/tex&amp;gt;. Если изначально предпосчитать проходом [[Обход_в_глубину,_цвета_вершин| dfs]] величины &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, то ответ на запрос &amp;lt;math&amp;gt;len(u, v)&amp;lt;/math&amp;gt; можно делать за время &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с &amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt;доп. памятью. Также можно воспользоваться техникой [[Сведение_задачи_LCA_к_задаче_RMQ|сведения задачи LCA к RMQ]] и решить с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; времени на запрос. Теперь научимся отвечать на запросы. Из последнего свойства центроидной декомпозиции видно, что если &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} искомая ближайшая помеченная вершина к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то путь между ними содержит центроид &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;, причем &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок одновременно вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве&amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Поэтому заведем [[Красно-черное_дерево|двоичное дерево поиска]] для каждого центроида &amp;lt;math&amp;gt;c \in T&amp;lt;/math&amp;gt;. В этой структуре для каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; будем хранить пары &amp;lt;math&amp;gt;(len(c, u), u)&amp;lt;/math&amp;gt; для всех помеченных вершин &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в поддереве центроидов &amp;lt;math&amp;gt;T(c)&amp;lt;/math&amp;gt;. Когда приходит запрос пометить вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} добавим в структуру данных для всех предков &amp;lt;math&amp;gt;p_c&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; пары &amp;lt;math&amp;gt;(len(p_c, v), v)&amp;lt;/math&amp;gt;. Мы совершим &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; добавлений, затратив &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; действий на каждое. Запрос снятия пометки с вершины обрабатывается аналогичными удалениями. Запрос поиска ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины {{---}} это запрос поиска вершины u, такой что  величина &amp;lt;math&amp;gt;len(c, u) + len(c, v)&amp;lt;/math&amp;gt; минимальна, где &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов (по пятому свойству, нас интересуют именно такие &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;). Этот запрос занимает так же &amp;lt;tex&amp;gt;O(log^2(n))&amp;lt;/tex&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
Итак, мы научились решать задачу с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и временной сложностью &amp;lt;math&amp;gt;O(log^2(n))&amp;lt;/math&amp;gt; на запрос любого типа с предварительным предпосчетом за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Варианты хранения центроидной декомпозиции ==&lt;br /&gt;
Для хранения дерева центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; есть &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; подхода, имеющих свои преимущества и недостатки :&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева запомним величину &amp;lt;math&amp;gt;p_v&amp;lt;/math&amp;gt; {{---}} номер предка вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Этот подход наиболее экономный по памяти (&amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;), но уступает в скорости и функциональности.&lt;br /&gt;
&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева будем хранить весь массив предков &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt; в дереве центроидов.&lt;br /&gt;
&lt;br /&gt;
Этот подход уступает в количестве необходимой дополнительной памяти (&amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt; суммарно), но имеет ряд преимуществ :&lt;br /&gt;
&lt;br /&gt;
1) При проходе по массиву предков фиксированной вершины будет выигрыш в скорости работы, т.к. весь массив будет лежать непрерывным блоком данных и следовательно будет закэширован&lt;br /&gt;
&lt;br /&gt;
2) На массиве предков можно строить различные структуры данных (такие как, например, дерево отрезков) для быстрого (в случае с деревом отрезков &amp;lt;math&amp;gt;O(log(log(n)))&amp;lt;/math&amp;gt; на запрос) поиска предка с необходимыми свойствами. Так, например, в описанной выше задаче про помеченные вершины наибольшего общего предка можно искать методом двоичных подъемов за &amp;lt;math&amp;gt;O(log(log(n))&amp;lt;/math&amp;gt; на запрос, т.к. размер массива предков есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; (по свойству 1 центроидной декомпозиции). Используя эту оптимизацию можно получить время &amp;lt;math&amp;gt;O(log(n)) + O(log(log(n)) = O(log(n))&amp;lt;/math&amp;gt; на запрос нахождения ближайшей помеченной вершины. Чтобы добиться улучшенной асимптотики для запросов изменения можно хранить дерево отрезков на каждом из путей &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt;, в каждой вершине которого хранить двоичное дерево поиска и поддерживать отложенные операции. Тогда ответ на эти запросы будет занимать &amp;lt;math&amp;gt;O(log(n) \cdot log(log(n))&amp;lt;/math&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
&lt;br /&gt;
*[[:Дерево_отрезков._Построение|Дерево отрезков]]&lt;br /&gt;
*[[:Heavy-light_декомпозиция|Heavy-light декомпозиция]]&lt;br /&gt;
*[[:Метод_двоичного_подъема| Метод двоичного подъема]]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы и структуры данных]]&lt;/div&gt;</summary>
		<author><name>92.255.113.52</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61623</id>
		<title>Centroid decomposition</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61623"/>
				<updated>2017-06-14T21:31:01Z</updated>
		
		<summary type="html">&lt;p&gt;92.255.113.52: /* Введение */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Centroid decomposition (рус. центроидная декомпозиция) {{---}} это структура данных, позволяющая отвечать на запросы на дереве. Чаще всего это запросы, связанные с нахождением функции на вершинах, связанных неравенством на расстояние между ними в дереве. Также иногда применяется для запросов на путях в дереве.&lt;br /&gt;
&lt;br /&gt;
== Введение ==&lt;br /&gt;
Рассмотрим &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; задачи на обычном массиве (в дальнейшем мы будем их обобщать на случай дерева):&lt;br /&gt;
&lt;br /&gt;
Задача &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть массив &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; положительных целых чисел из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов и числа &amp;lt;tex&amp;gt;W \geqslant 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;рl&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; индексов массива, таких что &amp;lt;tex&amp;gt;|j - i| \leqslant l &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\sum_{i=0}^{n - 1} a_i \leqslant W&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
Задача &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть прямая дорога, на которой расположены &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; городов. В некоторых городах есть госпитали, которые могут принимать больных. Поступают запросы вида :&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;, в котором находится больной и требуется найти такой город &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;|u - v|&amp;lt;/tex&amp;gt; минимально возможное.&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что больше он не будет принимать больных&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что теперь он может принимать больных&lt;br /&gt;
}}&lt;br /&gt;
Для начала решим обе задачи. &lt;br /&gt;
Первая задача решается методом [[Сортировка_слиянием|qevide&amp;amp;conqure (рус. разделяй и властвуй)]] {{---}} давайте разделим массив &amp;lt;tex&amp;gt;a[0 \dots n-1]&amp;lt;/tex&amp;gt; на 2 массива &amp;lt;tex&amp;gt;a[0\dots\frac{n}{2} - 1]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;a[\frac{n}{2} \dots n-1]&amp;lt;/tex&amp;gt; и рекурсивно решим задачу для каждого из них. Осталось научиться находить количество искомых пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt;, таких что &amp;lt;tex&amp;gt;i &amp;lt; \frac{n}{2}, j \geqslant \frac{n}{2}&amp;lt;/tex&amp;gt;. Для этого воспользуемся другой известной техникой {{---}} методом двух указателей. Посчитаем массив префиксных сумм для правой половины &amp;lt;tex&amp;gt;pref[i] = \sum_{j=\frac{n}{2}}^{i} a_j&amp;lt;/tex&amp;gt; и суффиксных (&amp;lt;tex&amp;gt;suf[i] = \sum_{j=i}^{\frac{n}{2} + 1} a_j&amp;lt;/tex&amp;gt;) {{---}} для левой. Заведем два указателя (&amp;lt;tex&amp;gt;p_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;p_2&amp;lt;/tex&amp;gt;). Изначально установим &amp;lt;tex&amp;gt;p_1 = \frac{n}{2} - l + 1, p_2 = \frac{n}{2}&amp;lt;/tex&amp;gt;. Пока &amp;lt;tex&amp;gt;p_2 - 1&amp;gt; \frac{n}{2}&amp;lt;/tex&amp;gt; и&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;pref[p_2] + suf[p_1] &amp;gt; W &amp;lt;/tex&amp;gt; будем уменьшать &amp;lt;math&amp;gt;p_2&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Если после этого &amp;lt;math&amp;gt;pref[p_2] + suf[p_1] \leqslant W&amp;lt;/math&amp;gt;, то к ответу прибавим &amp;lt;math&amp;gt;(p_2 - \frac{n}{2} + 1) * (\frac{n}{2} - p_1)&amp;lt;/math&amp;gt;, посго, увеличим &amp;lt;math&amp;gt;p_1&amp;lt;/math&amp;gt; на &amp;lt;math/math&amp;gt;. Так будем делать, пока &amp;lt;math&amp;gt;p_1 &amp;lt; \frac{n}{2}&amp;lt;/math&amp;gt;. В конце сложим текущий ответ и ответы для половин массива {{---}} получим ответ на задачу. Асимптотика такого алгоритма: &amp;lt;tex&amp;gt;T(n) = 2 * T(n / 2) + O(n) = O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вторая задача имеет запросы на изменение и поэтому надо применить динамическую версию qevide&amp;amp;conqure {{---}} [[Дерево_отрезков._Построение|дерево отрезков]]. Построим дерево отрезков,&lt;br /&gt;
поддерживающее 2 вида запросов: присвоение в точке и минимум на отрезке. Изначально сделаем так, чтобы дереву отрезков соответствовал массив &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;b_i = 0&amp;lt;/tex&amp;gt;, если в i-м городе принимает госпиталь и &amp;lt;tex&amp;gt;b_i = 1&amp;lt;/tex&amp;gt; иначе. Когда в каком-то городе открывается/закрывается госпиталь {{---}} делаем запрос на изменение в дереве отрезков. Когда требуется узнать ближайщий госпиталь к &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-му городу, можно воспользоваться одной из следующих идей: &lt;br /&gt;
а) (&amp;lt;tex&amp;gt;O(n \cdot log^2(n)&amp;lt;/tex&amp;gt;) Бинарным поиском ищем ближайший слева и ближайший справа к i-му городу госпиталь (такой город &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, что &amp;lt;tex&amp;gt;\min\limits_{k=i..j}b_k= 1&amp;lt;/tex&amp;gt;). Для этого внутри бинарного поиска каждый раз делаем запрос на поиск минимума в дереве отрезков.  &lt;br /&gt;
б) (&amp;lt;tex&amp;gt;O(n \cdot log(n)&amp;lt;/tex&amp;gt;) Будем одним спуском/подъемом по дереву определять, куда нам нужно идти (в левое или правое поддерево), тем самым делая одновоременно и бинарный поиск, и спуск/подъем по дереву.&lt;br /&gt;
&lt;br /&gt;
== Статическая центроидная декомпозиция ==&lt;br /&gt;
Перейдем к обобщению поставленных задач на случай дерева. Начнем, как и полагается, с первой:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть взвешенное дерево &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин, в каждой вершине которого написаны положительные целые числа. Также по-прежнему даны числа &amp;lt;tex&amp;gt;W &amp;gt;= 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; вершин дерева, таких что расстояние между ними не превосходит &amp;lt;math&amp;gt;l&amp;lt;/math&amp;gt; по числу ребер и не превосходит &amp;lt;math&amp;gt;W&amp;lt;/math&amp;gt; по сумме весов.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Для решения новой задачи применим ту же идею, что и была до этого {{---}} разделяй и властвуй. Для этого нам потребуется следующий объект :&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|id=191213&lt;br /&gt;
|definition =&lt;br /&gt;
'''Центроидом дерева''' (англ. ''centroid'') называется такая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, после удаления которой дерево разбивается на несколько (&amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;) поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, таких что для каждого &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;: &amp;lt;tex&amp;gt;|t_i| \leqslant \frac{n}{2}&amp;lt;/tex&amp;gt;, т.е. размер каждого поддерева не превосходит половины размера исходного дерева.&lt;br /&gt;
}}&lt;br /&gt;
Итак, в случае дерева идея разделяй-и-властвуй из предыдущего пункта будет формулироваться так: найдем центроид (доказательство её существования и алгоритм нахождение см. далее). Предположим, что мы сумели найти центроид за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; {{---}} размер дерева. Тогда, как и в упрощенной версии задачи {{---}} рекурсивно найдем ответ для всех поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, после чего попытаемся найти недостающие пары вершин, находящиехя в разных поддеревьях и удовлетворяющих вопросу задачи. Для этого будем отвечать на следующие запросы: пусть мы сейчас считаем все пары, где первая из вершин находится в поддереве &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и мы в некоторой структуре данных &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; храним все вершины остальных деревьев (каждую вершину задаем парой &amp;lt;math&amp;gt;(depth(v), length(v))&amp;lt;/math&amp;gt; {{---}} глубина вершины и длина пути до нее из корня поддерева), расстояние до которых от корня их поддерева не превышает &amp;lt;math&amp;gt;min(l, n)&amp;lt;/math&amp;gt;. Тогда просто пройдемся по всем вершинам &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; поддерева &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и прибавим к ответу число вершин в структуре &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt;, таких, что &amp;lt;tex&amp;gt;depth(u) \leqslant l - depth(v)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;length(u) \leqslant l - length(v)&amp;lt;/tex&amp;gt;. Это двумерные запросы, на которые можно отвечать за &amp;lt;math&amp;gt;O(log^2(n)&amp;lt;/math&amp;gt; с помощью [[Многомерное_дерево_отрезков|2d-дерева отрезков]], либо за &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с помощью [[Перечисление_точек_в_произвольном_прямоугольнике_за_n_*_log_%5E(d_-_1)_n_(range_tree)|техники поиска точек в d-мерном пространстве]]. Также читателю предлагается придумать и более эффективные и простые способы решить эту подзадачу.&lt;br /&gt;
&lt;br /&gt;
Оценим итоговую асимптотику: &amp;lt;tex&amp;gt;T(n) = k * T(n / k) + O(n \cdot log(n))&amp;lt;/tex&amp;gt;. Решая это рекурентное соотношение, получим &amp;lt;math&amp;gt;O(n \cdot log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь, как и было обещено, докажем лемму о существовании центроида и опишем алгоритм его эффективного поиска.&lt;br /&gt;
&lt;br /&gt;
=== Лемма о существовании центроида и алгоритм его нахождения. ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
В любом дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; существует центроид.&lt;br /&gt;
|proof=&lt;br /&gt;
Рассмотрим корень дерева &amp;lt;math&amp;gt;(r)&amp;lt;/math&amp;gt;. Положим изначально &amp;lt;math&amp;gt;v = r&amp;lt;/math&amp;gt;. Изначально &amp;lt;math&amp;gt;|subtree(v)| = n&amp;lt;/math&amp;gt;. Среди всех детей &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; выберем вершину &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; с максимальным размером поддерева. Если &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид, то положим &amp;lt;math&amp;gt;v = u&amp;lt;/math&amp;gt; и продолжим выбор нового u, иначе {{---}} остановимся. Докажем, что мы в какой-то момент остановимся. Пусть в призвольный момент времени &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид и размер её наддерева меньше &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, значит максимальное поддерево имеет размер больше чем &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, т.е. &amp;lt;math&amp;gt;|subtree(u)| &amp;gt; \frac{n}{2}&amp;lt;/math&amp;gt;, а значит размер &amp;quot;наддерева&amp;quot; вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; равен &amp;lt;tex&amp;gt;n - |subtree(u)| &amp;lt; \frac{n}{2}&amp;lt;/tex&amp;gt;. При этом теперь размер любого поддерева, на которое распадется дерево t при удалении вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; не превосходит &amp;lt;math&amp;gt;|subtree(u)| - 1&amp;lt;/math&amp;gt;, т.к. наддерево имеет размер меньше, чем поддерево &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, а любое поддерево вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; имеет хотя бы на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; вершину меньше (сама вершина &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;). По индукции получаем, что в любой момент времени размер наддерева вершины v меньше &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, значит мы будем спускаться только вниз по дереву &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, и при переходе к вершине &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} сыну &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; размер максимального поддерева уменьшится как минимум на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Значит не более чем за &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; шагов наши действия прекратятся и мы окажемся в центроиде дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
Итак, мы конструктивно доказали существование центроида и привели линейный относительно размера дерева алгоритм его нахождения. &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Реализация ===&lt;br /&gt;
&lt;br /&gt;
Поиск центроида в дереве:&lt;br /&gt;
&lt;br /&gt;
  '''int''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int''' sz[n])&lt;br /&gt;
     max_subtree = -1&lt;br /&gt;
     '''for''' u : children(v)&lt;br /&gt;
          '''if''' sz[u] &amp;gt; sz[v] / 2 and sz[u] &amp;gt; sz[max_subtree] &amp;lt;font color=green&amp;gt;// считаем sz[-1] = 0 &amp;lt;/font &amp;gt;&lt;br /&gt;
               max_subtree = u&lt;br /&gt;
     '''if''' max_subtree == -1&lt;br /&gt;
          return v&lt;br /&gt;
     '''else''' &lt;br /&gt;
          '''return''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz)&lt;br /&gt;
&lt;br /&gt;
Шаблон решения произвольной задачи на статическую центроидную декомпозицию:&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int''' sz[n])&lt;br /&gt;
     c = &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz) &amp;lt;font color=green&amp;gt;// находим c {{---}} центроид поддерева вершины v &amp;lt;/font &amp;gt;&lt;br /&gt;
     ch = children[c]&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{deleteEdges}&amp;lt;/tex&amp;gt;(c, children) &amp;lt;font color=green&amp;gt;// удаляем все ребра между вершиной с и детьми, чтобы мы не смогли из детей попасть в с. Это полезно делать, если решение  подзадачи для поддерева предполагает проход dfs &amp;lt;/font &amp;gt;&lt;br /&gt;
     '''for''' c2 : ch[c]&lt;br /&gt;
          &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;(children, c2, sz)&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{mergeSolution}&amp;lt;/tex&amp;gt;(children, c2) &amp;lt;font color=green&amp;gt;// решаем для текущего поддерева &amp;lt;/font &amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Динамическая центроидная декомпозиция (дерево центроидной декомпозиции) ==&lt;br /&gt;
Теперь вернемся ко второй задаче введения. Для ее решения мы пользовались динамичекой версией devide&amp;amp;conqure {{---}} деревом отрезков. В предыдущем пункте мы определили статический оналог devide&amp;amp;conqure для дерева. Теперь обобщим этот метод для динамических задач.&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Деревом центроидов (или центроидной декомпозицией)''' дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; называют дерево &amp;lt;math&amp;gt;T(t)&amp;lt;/math&amp;gt;, построенное на вершинах дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; следующим образом :&lt;br /&gt;
* Пусть вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} центроид дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Объявим его корнем нового дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пусть при удалении вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; из дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; оно распалось на поддеревья &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, тогда детьми вершины c объявим &amp;lt;tex&amp;gt;T(t_1), T(t_t),\dots, T(t_k)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
=== Свойства центроидной декомпозиции ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
'''Свойства центроидной декомпозиции''' :&lt;br /&gt;
* Глубина дерева центроидов не превосходит &amp;lt;tex&amp;gt;log(n)&amp;lt;/tex&amp;gt;. &lt;br /&gt;
* Каждая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; является центроидом одного из поддеревьев дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;&lt;br /&gt;
* Каждая вершина дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; поддеревьям дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (еще говорят, что вершина принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; центроидам дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, или что эти центроиды содержат вершину)&lt;br /&gt;
* Для любых вершин &amp;lt;tex&amp;gt;u, v \in T (u \neq v)&amp;lt;/tex&amp;gt; верно ровно одно из следующих трех утверждений: &lt;br /&gt;
a) &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
b) &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
c) &amp;lt;tex&amp;gt;T(u) \cap T(v) = \emptyset &amp;lt;/tex&amp;gt;&lt;br /&gt;
* Простой путь между любой парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; содержит центроид &amp;lt;tex&amp;gt;c \in T(t)&amp;lt;/tex&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
* Действительно, т.к. размер поддерева &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; не превосходит &amp;lt;tex&amp;gt;\frac{|subtree(c)|}{2}&amp;lt;/tex&amp;gt;, то при спуске в каждую следующую вершину на пути к любому листу в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; размер поддерева вершины, в которой мы сейчас находимся, уменьшается как минимум на &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;. Значит длина всего пути до листа не превосходит &amp;lt;math&amp;gt;log(n)&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
* Второе свойство очевидно из построения дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, т.к. если вершина принадлежит дереву центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то она является центроидом, а из построения &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы знаем, что каждая вершина исходного дерева принадлежит и дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Третье свойство {{---}} прямое следствие первых двух, т.к. вершина принадлежит любому центроиду &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; т.и т.т., когда c {{---}} отец вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов. Т.к. вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; точно принадлежит дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (свойство 2), то она лежит на каком-то пути в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, причем все ее родители (центроиды) ее содержат. А по свойству 1 длина любого вертикального (и даже простого) пути есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
* Четвертое свойство очевидно из того, что &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; {{---}} дерево. Т.к. &amp;lt;math&amp;gt;T(u)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;T(v)&amp;lt;/math&amp;gt; {{---}} поддеревья различных вершин дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то либо они не пересекаются, либо &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;, либо &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
* Для доказательства последнего свойства выберем в качестве вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;lca(u, v)&amp;lt;/math&amp;gt; в дереве центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Покажем, что так выбранная вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удовлетворяет заявленным свойствам. То, что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt; {{---}} очевидно по определению &amp;lt;math&amp;gt;lca&amp;lt;/math&amp;gt;, т.к. каждый предок любой вершины в дереве центроидов содержит эту вершину. Теперь докажем, что &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежит на пути между парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt;. Т.к. &amp;lt;math&amp;gt;c = lca(u, v)&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то из &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; нет ребра в такого сына, который содержит одновременно &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в своем поддереве (в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;), значит после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; разделится на несколько поддеревьев, таких что вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; окажутся в разных компонентах связности. А значит найдется такое ребро &amp;lt;math&amp;gt;(c, x)&amp;lt;/math&amp;gt;, которое принадлежало пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, но после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удалилось. Это доказывает то, что вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежала на пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
С помощью описанных свойств дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы можем решить задачу 2 для дерева :&lt;br /&gt;
&lt;br /&gt;
=== Пример решения задачи с помощью центроидной декомпозиции === &lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин. В каждый момент времени любая вершина дерева может быть либо помечена, либо нет. Изначально помечена только вершина номер &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Дан список из &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; запросов: &lt;br /&gt;
* Вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; пометили.&lt;br /&gt;
* С вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; сняли пометку.&lt;br /&gt;
* Найти номер ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Решение :&lt;br /&gt;
&lt;br /&gt;
Построим центроидную декомпозицию &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Изначально посчитаем для каждого центроида, содержащего вершину &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt; посчитаем расстояние до вершины &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Для этого воспользуемся [[Метод_двоичного_подъема|методом двоичных подъемов для поиска lca пары вершин в дереве]], а также тем фактом, что если глубина &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве определена как расстояние от корня до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то длина пути между парой вершин &amp;lt;math&amp;gt;(u, v)&amp;lt;/math&amp;gt; есть &amp;lt;tex&amp;gt;len(u, v) = h(u) + h(v) - 2 \cdot h(lca(u, v))&amp;lt;/tex&amp;gt;. Если изначально предпосчитать проходом [[Обход_в_глубину,_цвета_вершин| dfs]] величины &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, то ответ на запрос &amp;lt;math&amp;gt;len(u, v)&amp;lt;/math&amp;gt; можно делать за время &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с &amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt;доп. памятью. Также можно воспользоваться техникой [[Сведение_задачи_LCA_к_задаче_RMQ|сведения задачи LCA к RMQ]] и решить с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; времени на запрос. Теперь научимся отвечать на запросы. Из последнего свойства центроидной декомпозиции видно, что если &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} искомая ближайшая помеченная вершина к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то путь между ними содержит центроид &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;, причем &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок одновременно вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве&amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Поэтому заведем [[Красно-черное_дерево|двоичное дерево поиска]] для каждого центроида &amp;lt;math&amp;gt;c \in T&amp;lt;/math&amp;gt;. В этой структуре для каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; будем хранить пары &amp;lt;math&amp;gt;(len(c, u), u)&amp;lt;/math&amp;gt; для всех помеченных вершин &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в поддереве центроидов &amp;lt;math&amp;gt;T(c)&amp;lt;/math&amp;gt;. Когда приходит запрос пометить вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} добавим в структуру данных для всех предков &amp;lt;math&amp;gt;p_c&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; пары &amp;lt;math&amp;gt;(len(p_c, v), v)&amp;lt;/math&amp;gt;. Мы совершим &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; добавлений, затратив &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; действий на каждое. Запрос снятия пометки с вершины обрабатывается аналогичными удалениями. Запрос поиска ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины {{---}} это запрос поиска вершины u, такой что  величина &amp;lt;math&amp;gt;len(c, u) + len(c, v)&amp;lt;/math&amp;gt; минимальна, где &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов (по пятому свойству, нас интересуют именно такие &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;). Этот запрос занимает так же &amp;lt;tex&amp;gt;O(log^2(n))&amp;lt;/tex&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
Итак, мы научились решать задачу с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и временной сложностью &amp;lt;math&amp;gt;O(log^2(n))&amp;lt;/math&amp;gt; на запрос любого типа с предварительным предпосчетом за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Варианты хранения центроидной декомпозиции ==&lt;br /&gt;
Для хранения дерева центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; есть &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; подхода, имеющих свои преимущества и недостатки :&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева запомним величину &amp;lt;math&amp;gt;p_v&amp;lt;/math&amp;gt; {{---}} номер предка вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Этот подход наиболее экономный по памяти (&amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;), но уступает в скорости и функциональности.&lt;br /&gt;
&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева будем хранить весь массив предков &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt; в дереве центроидов.&lt;br /&gt;
&lt;br /&gt;
Этот подход уступает в количестве необходимой дополнительной памяти (&amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt; суммарно), но имеет ряд преимуществ :&lt;br /&gt;
&lt;br /&gt;
1) При проходе по массиву предков фиксированной вершины будет выигрыш в скорости работы, т.к. весь массив будет лежать непрерывным блоком данных и следовательно будет закэширован&lt;br /&gt;
&lt;br /&gt;
2) На массиве предков можно строить различные структуры данных (такие как, например, дерево отрезков) для быстрого (в случае с деревом отрезков &amp;lt;math&amp;gt;O(log(log(n)))&amp;lt;/math&amp;gt; на запрос) поиска предка с необходимыми свойствами. Так, например, в описанной выше задаче про помеченные вершины наибольшего общего предка можно искать методом двоичных подъемов за &amp;lt;math&amp;gt;O(log(log(n))&amp;lt;/math&amp;gt; на запрос, т.к. размер массива предков есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; (по свойству 1 центроидной декомпозиции). Используя эту оптимизацию можно получить время &amp;lt;math&amp;gt;O(log(n)) + O(log(log(n)) = O(log(n))&amp;lt;/math&amp;gt; на запрос нахождения ближайшей помеченной вершины. Чтобы добиться улучшенной асимптотики для запросов изменения можно хранить дерево отрезков на каждом из путей &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt;, в каждой вершине которого хранить двоичное дерево поиска и поддерживать отложенные операции. Тогда ответ на эти запросы будет занимать &amp;lt;math&amp;gt;O(log(n) \cdot log(log(n))&amp;lt;/math&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
&lt;br /&gt;
*[[:Дерево_отрезков._Построение|Дерево отрезков]]&lt;br /&gt;
*[[:Heavy-light_декомпозиция|Heavy-light декомпозиция]]&lt;br /&gt;
*[[:Метод_двоичного_подъема| Метод двоичного подъема]]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы и структуры данных]]&lt;/div&gt;</summary>
		<author><name>92.255.113.52</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61622</id>
		<title>Centroid decomposition</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61622"/>
				<updated>2017-06-14T21:29:57Z</updated>
		
		<summary type="html">&lt;p&gt;92.255.113.52: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Centroid decomposition (рус. центроидная декомпозиция) {{---}} это структура данных, позволяющая отвечать на запросы на дереве. Чаще всего это запросы, связанные с нахождением функции на вершинах, связанных неравенством на расстояние между ними в дереве. Также иногда применяется для запросов на путях в дереве.&lt;br /&gt;
&lt;br /&gt;
== Введение ==&lt;br /&gt;
Рассмотрим &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; задачи на обычном массиве (в дальнейшем мы будем их обобщать на случай дерева):&lt;br /&gt;
&lt;br /&gt;
Задача &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть массив &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; положительных целых чисел из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов и числа &amp;lt;tex&amp;gt;W \geqslant 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; индексов массива, таких что &amp;lt;tex&amp;gt;|j - i| \leqslant l &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\sum_{i=0}^{n - 1} a_i \leqslant W&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
Задача &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть прямая дорога, на которой расположены &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; городов. В некоторых городах есть госпитали, которые могут принимать больных. Поступают запросы вида :&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;, в котором находится больной и требуется найти такой город &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;|u - v|&amp;lt;/tex&amp;gt; минимально возможное.&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что больше он не будет принимать больных&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что теперь он может принимать больных&lt;br /&gt;
}}&lt;br /&gt;
Для начала решим обе задачи. &lt;br /&gt;
Первая задача решается методом [[Сортировка_слиянием|qevide&amp;amp;conqure (рус. разделяй и властвуй)]] {{---}} давайте разделим массив &amp;lt;tex&amp;gt;a[0\dotsn-1]&amp;lt;/tex&amp;gt; на 2 массива &amp;lt;tex&amp;gt;a[0\dots\frac{n}{2} - 1]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;a[\frac{n}{2} \dots n-1]&amp;lt;/tex&amp;gt; и рекурсивно решим задачу для каждого из них. Осталось научиться находить количество искомых пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt;, таких что &amp;lt;tex&amp;gt;i &amp;lt; \frac{n}{2}, j \geqslant \frac{n}{2}&amp;lt;/tex&amp;gt;. Для этого воспользуемся другой известной техникой {{---}} методом двух указателей. Посчитаем массив префиксных сумм для правой половины &amp;lt;tex&amp;gt;pref[i] = \sum_{j=\frac{n}{2}}^{i} a_j&amp;lt;/tex&amp;gt; и суффиксных (&amp;lt;tex&amp;gt;suf[i] = \sum_{j=i}^{\frac{n}{2} + 1} a_j&amp;lt;/tex&amp;gt;) {{---}} для левой. Заведем два указателя (&amp;lt;tex&amp;gt;p_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;p_2&amp;lt;/tex&amp;gt;). Изначально установим &amp;lt;tex&amp;gt;p_1 = \frac{n}{2} - l + 1, p_2 = \frac{n}{2}&amp;lt;/tex&amp;gt;. Пока &amp;lt;tex&amp;gt;p_2 - 1&amp;gt; \frac{n}{2}&amp;lt;/tex&amp;gt; и&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;pref[p_2] + suf[p_1] &amp;gt; W &amp;lt;/tex&amp;gt; будем уменьшать &amp;lt;math&amp;gt;p_2&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Если после этого &amp;lt;math&amp;gt;pref[p_2] + suf[p_1] \leqslant W&amp;lt;/math&amp;gt;, то к ответу прибавим &amp;lt;math&amp;gt;(p_2 - \frac{n}{2} + 1) * (\frac{n}{2} - p_1)&amp;lt;/math&amp;gt;, посго, увеличим &amp;lt;math&amp;gt;p_1&amp;lt;/math&amp;gt; на &amp;lt;math/math&amp;gt;. Так будем делать, пока &amp;lt;math&amp;gt;p_1 &amp;lt; \frac{n}{2}&amp;lt;/math&amp;gt;. В конце сложим текущий ответ и ответы для половин массива {{---}} получим ответ на задачу. Асимптотика такого алгоритма: &amp;lt;tex&amp;gt;T(n) = 2 * T(n / 2) + O(n) = O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вторая задача имеет запросы на изменение и поэтому надо применить динамическую версию qevide&amp;amp;conqure {{---}} [[Дерево_отрезков._Построение|дерево отрезков]]. Построим дерево отрезков,&lt;br /&gt;
поддерживающее 2 вида запросов: присвоение в точке и минимум на отрезке. Изначально сделаем так, чтобы дереву отрезков соответствовал массив &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;b_i = 0&amp;lt;/tex&amp;gt;, если в i-м городе принимает госпиталь и &amp;lt;tex&amp;gt;b_i = 1&amp;lt;/tex&amp;gt; иначе. Когда в каком-то городе открывается/закрывается госпиталь {{---}} делаем запрос на изменение в дереве отрезков. Когда требуется узнать ближайщий госпиталь к &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-му городу, можно воспользоваться одной из следующих идей: &lt;br /&gt;
а) (&amp;lt;tex&amp;gt;O(n \cdot log^2(n)&amp;lt;/tex&amp;gt;) Бинарным поиском ищем ближайший слева и ближайший справа к i-му городу госпиталь (такой город &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, что &amp;lt;tex&amp;gt;\min\limits_{k=i..j}b_k= 1&amp;lt;/tex&amp;gt;). Для этого внутри бинарного поиска каждый раз делаем запрос на поиск минимума в дереве отрезков.  &lt;br /&gt;
б) (&amp;lt;tex&amp;gt;O(n \cdot log(n)&amp;lt;/tex&amp;gt;) Будем одним спуском/подъемом по дереву определять, куда нам нужно идти (в левое или правое поддерево), тем самым делая одновоременно и бинарный поиск, и спуск/подъем по дереву.&lt;br /&gt;
&lt;br /&gt;
== Статическая центроидная декомпозиция ==&lt;br /&gt;
Перейдем к обобщению поставленных задач на случай дерева. Начнем, как и полагается, с первой:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть взвешенное дерево &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин, в каждой вершине которого написаны положительные целые числа. Также по-прежнему даны числа &amp;lt;tex&amp;gt;W &amp;gt;= 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; вершин дерева, таких что расстояние между ними не превосходит &amp;lt;math&amp;gt;l&amp;lt;/math&amp;gt; по числу ребер и не превосходит &amp;lt;math&amp;gt;W&amp;lt;/math&amp;gt; по сумме весов.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Для решения новой задачи применим ту же идею, что и была до этого {{---}} разделяй и властвуй. Для этого нам потребуется следующий объект :&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|id=191213&lt;br /&gt;
|definition =&lt;br /&gt;
'''Центроидом дерева''' (англ. ''centroid'') называется такая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, после удаления которой дерево разбивается на несколько (&amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;) поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, таких что для каждого &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;: &amp;lt;tex&amp;gt;|t_i| \leqslant \frac{n}{2}&amp;lt;/tex&amp;gt;, т.е. размер каждого поддерева не превосходит половины размера исходного дерева.&lt;br /&gt;
}}&lt;br /&gt;
Итак, в случае дерева идея разделяй-и-властвуй из предыдущего пункта будет формулироваться так: найдем центроид (доказательство её существования и алгоритм нахождение см. далее). Предположим, что мы сумели найти центроид за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; {{---}} размер дерева. Тогда, как и в упрощенной версии задачи {{---}} рекурсивно найдем ответ для всех поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, после чего попытаемся найти недостающие пары вершин, находящиехя в разных поддеревьях и удовлетворяющих вопросу задачи. Для этого будем отвечать на следующие запросы: пусть мы сейчас считаем все пары, где первая из вершин находится в поддереве &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и мы в некоторой структуре данных &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; храним все вершины остальных деревьев (каждую вершину задаем парой &amp;lt;math&amp;gt;(depth(v), length(v))&amp;lt;/math&amp;gt; {{---}} глубина вершины и длина пути до нее из корня поддерева), расстояние до которых от корня их поддерева не превышает &amp;lt;math&amp;gt;min(l, n)&amp;lt;/math&amp;gt;. Тогда просто пройдемся по всем вершинам &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; поддерева &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и прибавим к ответу число вершин в структуре &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt;, таких, что &amp;lt;tex&amp;gt;depth(u) \leqslant l - depth(v)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;length(u) \leqslant l - length(v)&amp;lt;/tex&amp;gt;. Это двумерные запросы, на которые можно отвечать за &amp;lt;math&amp;gt;O(log^2(n)&amp;lt;/math&amp;gt; с помощью [[Многомерное_дерево_отрезков|2d-дерева отрезков]], либо за &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с помощью [[Перечисление_точек_в_произвольном_прямоугольнике_за_n_*_log_%5E(d_-_1)_n_(range_tree)|техники поиска точек в d-мерном пространстве]]. Также читателю предлагается придумать и более эффективные и простые способы решить эту подзадачу.&lt;br /&gt;
&lt;br /&gt;
Оценим итоговую асимптотику: &amp;lt;tex&amp;gt;T(n) = k * T(n / k) + O(n \cdot log(n))&amp;lt;/tex&amp;gt;. Решая это рекурентное соотношение, получим &amp;lt;math&amp;gt;O(n \cdot log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь, как и было обещено, докажем лемму о существовании центроида и опишем алгоритм его эффективного поиска.&lt;br /&gt;
&lt;br /&gt;
=== Лемма о существовании центроида и алгоритм его нахождения. ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
В любом дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; существует центроид.&lt;br /&gt;
|proof=&lt;br /&gt;
Рассмотрим корень дерева &amp;lt;math&amp;gt;(r)&amp;lt;/math&amp;gt;. Положим изначально &amp;lt;math&amp;gt;v = r&amp;lt;/math&amp;gt;. Изначально &amp;lt;math&amp;gt;|subtree(v)| = n&amp;lt;/math&amp;gt;. Среди всех детей &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; выберем вершину &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; с максимальным размером поддерева. Если &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид, то положим &amp;lt;math&amp;gt;v = u&amp;lt;/math&amp;gt; и продолжим выбор нового u, иначе {{---}} остановимся. Докажем, что мы в какой-то момент остановимся. Пусть в призвольный момент времени &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид и размер её наддерева меньше &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, значит максимальное поддерево имеет размер больше чем &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, т.е. &amp;lt;math&amp;gt;|subtree(u)| &amp;gt; \frac{n}{2}&amp;lt;/math&amp;gt;, а значит размер &amp;quot;наддерева&amp;quot; вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; равен &amp;lt;tex&amp;gt;n - |subtree(u)| &amp;lt; \frac{n}{2}&amp;lt;/tex&amp;gt;. При этом теперь размер любого поддерева, на которое распадется дерево t при удалении вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; не превосходит &amp;lt;math&amp;gt;|subtree(u)| - 1&amp;lt;/math&amp;gt;, т.к. наддерево имеет размер меньше, чем поддерево &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, а любое поддерево вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; имеет хотя бы на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; вершину меньше (сама вершина &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;). По индукции получаем, что в любой момент времени размер наддерева вершины v меньше &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, значит мы будем спускаться только вниз по дереву &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, и при переходе к вершине &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} сыну &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; размер максимального поддерева уменьшится как минимум на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Значит не более чем за &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; шагов наши действия прекратятся и мы окажемся в центроиде дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
Итак, мы конструктивно доказали существование центроида и привели линейный относительно размера дерева алгоритм его нахождения. &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Реализация ===&lt;br /&gt;
&lt;br /&gt;
Поиск центроида в дереве:&lt;br /&gt;
&lt;br /&gt;
  '''int''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int''' sz[n])&lt;br /&gt;
     max_subtree = -1&lt;br /&gt;
     '''for''' u : children(v)&lt;br /&gt;
          '''if''' sz[u] &amp;gt; sz[v] / 2 and sz[u] &amp;gt; sz[max_subtree] &amp;lt;font color=green&amp;gt;// считаем sz[-1] = 0 &amp;lt;/font &amp;gt;&lt;br /&gt;
               max_subtree = u&lt;br /&gt;
     '''if''' max_subtree == -1&lt;br /&gt;
          return v&lt;br /&gt;
     '''else''' &lt;br /&gt;
          '''return''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz)&lt;br /&gt;
&lt;br /&gt;
Шаблон решения произвольной задачи на статическую центроидную декомпозицию:&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int''' sz[n])&lt;br /&gt;
     c = &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz) &amp;lt;font color=green&amp;gt;// находим c {{---}} центроид поддерева вершины v &amp;lt;/font &amp;gt;&lt;br /&gt;
     ch = children[c]&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{deleteEdges}&amp;lt;/tex&amp;gt;(c, children) &amp;lt;font color=green&amp;gt;// удаляем все ребра между вершиной с и детьми, чтобы мы не смогли из детей попасть в с. Это полезно делать, если решение  подзадачи для поддерева предполагает проход dfs &amp;lt;/font &amp;gt;&lt;br /&gt;
     '''for''' c2 : ch[c]&lt;br /&gt;
          &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;(children, c2, sz)&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{mergeSolution}&amp;lt;/tex&amp;gt;(children, c2) &amp;lt;font color=green&amp;gt;// решаем для текущего поддерева &amp;lt;/font &amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Динамическая центроидная декомпозиция (дерево центроидной декомпозиции) ==&lt;br /&gt;
Теперь вернемся ко второй задаче введения. Для ее решения мы пользовались динамичекой версией devide&amp;amp;conqure {{---}} деревом отрезков. В предыдущем пункте мы определили статический оналог devide&amp;amp;conqure для дерева. Теперь обобщим этот метод для динамических задач.&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Деревом центроидов (или центроидной декомпозицией)''' дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; называют дерево &amp;lt;math&amp;gt;T(t)&amp;lt;/math&amp;gt;, построенное на вершинах дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; следующим образом :&lt;br /&gt;
* Пусть вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} центроид дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Объявим его корнем нового дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пусть при удалении вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; из дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; оно распалось на поддеревья &amp;lt;tex&amp;gt;t_1, t_2,\dots, t_k&amp;lt;/tex&amp;gt;, тогда детьми вершины c объявим &amp;lt;tex&amp;gt;T(t_1), T(t_t),\dots, T(t_k)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
=== Свойства центроидной декомпозиции ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
'''Свойства центроидной декомпозиции''' :&lt;br /&gt;
* Глубина дерева центроидов не превосходит &amp;lt;tex&amp;gt;log(n)&amp;lt;/tex&amp;gt;. &lt;br /&gt;
* Каждая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; является центроидом одного из поддеревьев дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;&lt;br /&gt;
* Каждая вершина дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; поддеревьям дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (еще говорят, что вершина принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; центроидам дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, или что эти центроиды содержат вершину)&lt;br /&gt;
* Для любых вершин &amp;lt;tex&amp;gt;u, v \in T (u \neq v)&amp;lt;/tex&amp;gt; верно ровно одно из следующих трех утверждений: &lt;br /&gt;
a) &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
b) &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
c) &amp;lt;tex&amp;gt;T(u) \cap T(v) = \emptyset &amp;lt;/tex&amp;gt;&lt;br /&gt;
* Простой путь между любой парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; содержит центроид &amp;lt;tex&amp;gt;c \in T(t)&amp;lt;/tex&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
* Действительно, т.к. размер поддерева &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; не превосходит &amp;lt;tex&amp;gt;\frac{|subtree(c)|}{2}&amp;lt;/tex&amp;gt;, то при спуске в каждую следующую вершину на пути к любому листу в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; размер поддерева вершины, в которой мы сейчас находимся, уменьшается как минимум на &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;. Значит длина всего пути до листа не превосходит &amp;lt;math&amp;gt;log(n)&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
* Второе свойство очевидно из построения дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, т.к. если вершина принадлежит дереву центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то она является центроидом, а из построения &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы знаем, что каждая вершина исходного дерева принадлежит и дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Третье свойство {{---}} прямое следствие первых двух, т.к. вершина принадлежит любому центроиду &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; т.и т.т., когда c {{---}} отец вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов. Т.к. вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; точно принадлежит дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (свойство 2), то она лежит на каком-то пути в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, причем все ее родители (центроиды) ее содержат. А по свойству 1 длина любого вертикального (и даже простого) пути есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
* Четвертое свойство очевидно из того, что &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; {{---}} дерево. Т.к. &amp;lt;math&amp;gt;T(u)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;T(v)&amp;lt;/math&amp;gt; {{---}} поддеревья различных вершин дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то либо они не пересекаются, либо &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;, либо &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
* Для доказательства последнего свойства выберем в качестве вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;lca(u, v)&amp;lt;/math&amp;gt; в дереве центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Покажем, что так выбранная вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удовлетворяет заявленным свойствам. То, что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt; {{---}} очевидно по определению &amp;lt;math&amp;gt;lca&amp;lt;/math&amp;gt;, т.к. каждый предок любой вершины в дереве центроидов содержит эту вершину. Теперь докажем, что &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежит на пути между парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt;. Т.к. &amp;lt;math&amp;gt;c = lca(u, v)&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то из &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; нет ребра в такого сына, который содержит одновременно &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в своем поддереве (в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;), значит после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; разделится на несколько поддеревьев, таких что вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; окажутся в разных компонентах связности. А значит найдется такое ребро &amp;lt;math&amp;gt;(c, x)&amp;lt;/math&amp;gt;, которое принадлежало пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, но после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удалилось. Это доказывает то, что вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежала на пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
С помощью описанных свойств дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы можем решить задачу 2 для дерева :&lt;br /&gt;
&lt;br /&gt;
=== Пример решения задачи с помощью центроидной декомпозиции === &lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин. В каждый момент времени любая вершина дерева может быть либо помечена, либо нет. Изначально помечена только вершина номер &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Дан список из &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; запросов: &lt;br /&gt;
* Вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; пометили.&lt;br /&gt;
* С вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; сняли пометку.&lt;br /&gt;
* Найти номер ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Решение :&lt;br /&gt;
&lt;br /&gt;
Построим центроидную декомпозицию &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Изначально посчитаем для каждого центроида, содержащего вершину &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt; посчитаем расстояние до вершины &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Для этого воспользуемся [[Метод_двоичного_подъема|методом двоичных подъемов для поиска lca пары вершин в дереве]], а также тем фактом, что если глубина &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве определена как расстояние от корня до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то длина пути между парой вершин &amp;lt;math&amp;gt;(u, v)&amp;lt;/math&amp;gt; есть &amp;lt;tex&amp;gt;len(u, v) = h(u) + h(v) - 2 \cdot h(lca(u, v))&amp;lt;/tex&amp;gt;. Если изначально предпосчитать проходом [[Обход_в_глубину,_цвета_вершин| dfs]] величины &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, то ответ на запрос &amp;lt;math&amp;gt;len(u, v)&amp;lt;/math&amp;gt; можно делать за время &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с &amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt;доп. памятью. Также можно воспользоваться техникой [[Сведение_задачи_LCA_к_задаче_RMQ|сведения задачи LCA к RMQ]] и решить с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; времени на запрос. Теперь научимся отвечать на запросы. Из последнего свойства центроидной декомпозиции видно, что если &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} искомая ближайшая помеченная вершина к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то путь между ними содержит центроид &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;, причем &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок одновременно вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве&amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Поэтому заведем [[Красно-черное_дерево|двоичное дерево поиска]] для каждого центроида &amp;lt;math&amp;gt;c \in T&amp;lt;/math&amp;gt;. В этой структуре для каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; будем хранить пары &amp;lt;math&amp;gt;(len(c, u), u)&amp;lt;/math&amp;gt; для всех помеченных вершин &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в поддереве центроидов &amp;lt;math&amp;gt;T(c)&amp;lt;/math&amp;gt;. Когда приходит запрос пометить вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} добавим в структуру данных для всех предков &amp;lt;math&amp;gt;p_c&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; пары &amp;lt;math&amp;gt;(len(p_c, v), v)&amp;lt;/math&amp;gt;. Мы совершим &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; добавлений, затратив &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; действий на каждое. Запрос снятия пометки с вершины обрабатывается аналогичными удалениями. Запрос поиска ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины {{---}} это запрос поиска вершины u, такой что  величина &amp;lt;math&amp;gt;len(c, u) + len(c, v)&amp;lt;/math&amp;gt; минимальна, где &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; {{---}} предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов (по пятому свойству, нас интересуют именно такие &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;). Этот запрос занимает так же &amp;lt;tex&amp;gt;O(log^2(n))&amp;lt;/tex&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
Итак, мы научились решать задачу с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и временной сложностью &amp;lt;math&amp;gt;O(log^2(n))&amp;lt;/math&amp;gt; на запрос любого типа с предварительным предпосчетом за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Варианты хранения центроидной декомпозиции ==&lt;br /&gt;
Для хранения дерева центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; есть &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; подхода, имеющих свои преимущества и недостатки :&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева запомним величину &amp;lt;math&amp;gt;p_v&amp;lt;/math&amp;gt; {{---}} номер предка вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Этот подход наиболее экономный по памяти (&amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;), но уступает в скорости и функциональности.&lt;br /&gt;
&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева будем хранить весь массив предков &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt; в дереве центроидов.&lt;br /&gt;
&lt;br /&gt;
Этот подход уступает в количестве необходимой дополнительной памяти (&amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt; суммарно), но имеет ряд преимуществ :&lt;br /&gt;
&lt;br /&gt;
1) При проходе по массиву предков фиксированной вершины будет выигрыш в скорости работы, т.к. весь массив будет лежать непрерывным блоком данных и следовательно будет закэширован&lt;br /&gt;
&lt;br /&gt;
2) На массиве предков можно строить различные структуры данных (такие как, например, дерево отрезков) для быстрого (в случае с деревом отрезков &amp;lt;math&amp;gt;O(log(log(n)))&amp;lt;/math&amp;gt; на запрос) поиска предка с необходимыми свойствами. Так, например, в описанной выше задаче про помеченные вершины наибольшего общего предка можно искать методом двоичных подъемов за &amp;lt;math&amp;gt;O(log(log(n))&amp;lt;/math&amp;gt; на запрос, т.к. размер массива предков есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; (по свойству 1 центроидной декомпозиции). Используя эту оптимизацию можно получить время &amp;lt;math&amp;gt;O(log(n)) + O(log(log(n)) = O(log(n))&amp;lt;/math&amp;gt; на запрос нахождения ближайшей помеченной вершины. Чтобы добиться улучшенной асимптотики для запросов изменения можно хранить дерево отрезков на каждом из путей &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt;, в каждой вершине которого хранить двоичное дерево поиска и поддерживать отложенные операции. Тогда ответ на эти запросы будет занимать &amp;lt;math&amp;gt;O(log(n) \cdot log(log(n))&amp;lt;/math&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
&lt;br /&gt;
*[[:Дерево_отрезков._Построение|Дерево отрезков]]&lt;br /&gt;
*[[:Heavy-light_декомпозиция|Heavy-light декомпозиция]]&lt;br /&gt;
*[[:Метод_двоичного_подъема| Метод двоичного подъема]]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы и структуры данных]]&lt;/div&gt;</summary>
		<author><name>92.255.113.52</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61621</id>
		<title>Centroid decomposition</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61621"/>
				<updated>2017-06-14T21:25:37Z</updated>
		
		<summary type="html">&lt;p&gt;92.255.113.52: /* Пример решения задачи с помощью центроидной декомпозиции */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Centroid decomposition (рус. центроидная декомпозиция) {{---}} это структура данных, позволяющая отвечать на запросы на дереве. Чаще всего это запросы, связанные с нахождением функции на вершинах, связанных неравенством на расстояние между ними в дереве. Также иногда применяется для запросов на путях в дереве.&lt;br /&gt;
&lt;br /&gt;
== Введение ==&lt;br /&gt;
Рассмотрим &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; задачи на обычном массиве (в дальнейшем мы будем их обобщать на случай дерева):&lt;br /&gt;
&lt;br /&gt;
Задача &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть массив &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; положительных целых чисел из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов и числа &amp;lt;tex&amp;gt;W \geqslant 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; индексов массива, таких что &amp;lt;tex&amp;gt;|j - i| \leqslant l &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\sum_{i=0}^{n - 1} a_i \leqslant W&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
Задача &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть прямая дорога, на которой расположены &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; городов. В некоторых городах есть госпитали, которые могут принимать больных. Поступают запросы вида :&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;, в котором находится больной и требуется найти такой город &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;|u - v|&amp;lt;/tex&amp;gt; минимально возможное.&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что больше он не будет принимать больных&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что теперь он может принимать больных&lt;br /&gt;
}}&lt;br /&gt;
Для начала решим обе задачи. &lt;br /&gt;
Первая задача решается методом [[Сортировка_слиянием|qevide&amp;amp;conqure (рус. разделяй и властвуй)]] {{---}} давайте разделим массив &amp;lt;tex&amp;gt;a[0...n-1]&amp;lt;/tex&amp;gt; на 2 массива &amp;lt;tex&amp;gt;a[0...\frac{n}{2} - 1]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;a[\frac{n}{2}...n-1]&amp;lt;/tex&amp;gt; и рекурсивно решим задачу для каждого из них. Осталось научиться находить количество искомых пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt;, таких что &amp;lt;tex&amp;gt;i &amp;lt; \frac{n}{2}, j \geqslant \frac{n}{2}&amp;lt;/tex&amp;gt;. Для этого воспользуемся другой известной техникой {{---}} методом двух указателей. Посчитаем массив префиксных сумм для правой половины &amp;lt;tex&amp;gt;pref[i] = \sum_{j=\frac{n}{2}}^{i} a_j&amp;lt;/tex&amp;gt; и суффиксных (&amp;lt;tex&amp;gt;suf[i] = \sum_{j=i}^{\frac{n}{2} + 1} a_j&amp;lt;/tex&amp;gt;) {{---}} для левой. Заведем два указателя (&amp;lt;tex&amp;gt;p_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;p_2&amp;lt;/tex&amp;gt;). Изначально установим &amp;lt;tex&amp;gt;p_1 = \frac{n}{2} - l + 1, p_2 = \frac{n}{2}&amp;lt;/tex&amp;gt;. Пока &amp;lt;tex&amp;gt;p_2 - 1&amp;gt; \frac{n}{2}&amp;lt;/tex&amp;gt; и&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;pref[p_2] + suf[p_1] &amp;gt; W &amp;lt;/tex&amp;gt; будем уменьшать &amp;lt;math&amp;gt;p_2&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Если после этого &amp;lt;math&amp;gt;pref[p_2] + suf[p_1] \leqslant W&amp;lt;/math&amp;gt;, то к ответу прибавим &amp;lt;math&amp;gt;(p_2 - \frac{n}{2} + 1) * (\frac{n}{2} - p_1)&amp;lt;/math&amp;gt;, посго, увеличим &amp;lt;math&amp;gt;p_1&amp;lt;/math&amp;gt; на &amp;lt;math/math&amp;gt;. Так будем делать, пока &amp;lt;math&amp;gt;p_1 &amp;lt; \frac{n}{2}&amp;lt;/math&amp;gt;. В конце сложим текущий ответ и ответы для половин массива {{---}} получим ответ на задачу. Асимптотика такого алгоритма : &amp;lt;tex&amp;gt;T(n) = 2 * T(n / 2) + O(n) = O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вторая задача имеет запросы на изменение и поэтому надо применить динамическую версию qevide&amp;amp;conqure {{---}} [[Дерево_отрезков._Построение|дерево отрезков]]. Построим дерево отрезков,&lt;br /&gt;
поддерживающее 2 вида запросов : присвоение в точке и минимум на отрезке. Изначально сделаем так, чтобы дереву отрезков соответствовал массив &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;b_i = 0&amp;lt;/tex&amp;gt;, если в i-м городе принимает госпиталь и &amp;lt;tex&amp;gt;b_i = 1&amp;lt;/tex&amp;gt; иначе. Когда в каком-то городе открывается/закрывается госпиталь {{---}} делаем запрос на изменение в дереве отрезков. Когда требуется узнать ближайщий госпиталь к &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-му городу, можно воспользоваться одной из следующих идей : &lt;br /&gt;
а) (&amp;lt;tex&amp;gt;O(n \cdot log^2(n)&amp;lt;/tex&amp;gt;) Бинарным поиском ищем ближайший слева и ближайший справа к i-му городу госпиталь (такой город &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, что &amp;lt;tex&amp;gt;\min\limits_{k=i..j}b_k= 1&amp;lt;/tex&amp;gt;). Для этого внутри бинарного поиска каждый раз делаем запрос на поиск минимума в дереве отрезков.  &lt;br /&gt;
б) (&amp;lt;tex&amp;gt;O(n \cdot log(n)&amp;lt;/tex&amp;gt;) Будем одним спуском/подъемом по дереву определять, куда нам нужно идти (в левое или правое поддерево), тем самым делая одновоременно и бинарный поиск, и спуск/подъем по дереву.&lt;br /&gt;
&lt;br /&gt;
== Статическая центроидная декомпозиция ==&lt;br /&gt;
Перейдем к обобщению поставленных задач на случай дерева. Начнем, как и полагается, с первой:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть взвешенное дерево &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин, в каждой вершине которого написаны положительные целые числа. Также по-прежнему даны числа &amp;lt;tex&amp;gt;W &amp;gt;= 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; вершин дерева, таких что расстояние между ними не превосходит &amp;lt;math&amp;gt;l&amp;lt;/math&amp;gt; по числу ребер и не превосходит &amp;lt;math&amp;gt;W&amp;lt;/math&amp;gt; по сумме весов.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Для решения новой задачи применим ту же идею, что и была до этого {{---}} разделяй и властвуй. Для этого нам потребуется следующий объект :&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|id=191213&lt;br /&gt;
|definition =&lt;br /&gt;
'''Центроидом дерева''' (англ. ''centroid'') называется такая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, после удаления которой дерево разбивается на несколько (&amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;) поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,..., t_k&amp;lt;/tex&amp;gt;, таких что для каждого &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; : &amp;lt;tex&amp;gt;|t_i| \leqslant \frac{n}{2}&amp;lt;/tex&amp;gt;, т.е. размер каждого поддерева не превосходит половины размера исходного дерева.&lt;br /&gt;
}}&lt;br /&gt;
Итак, в случае дерева идея разделяй-и-властвуй из предыдущего пункта будет формулироваться так : найдем центроид (доказательство её существования и алгоритм нахождение см. далее). Предположим, что мы сумели найти центроид за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; {{---}} размер дерева. Тогда, как и в упрощенной версии задачи {{---}} рекурсивно найдем ответ для всех поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,..., t_k&amp;lt;/tex&amp;gt;, после чего попытаемся найти недостающие пары вершин, находящиехя в разных поддеревьях и удовлетворяющих вопросу задачи. Для этого будем отвечать на следующие запросы : пусть мы сейчас считаем все пары, где первая из вершин находится в поддереве &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и мы в некоторой структуре данных &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; храним все вершины остальных деревьев (каждую вершину задаем парой &amp;lt;math&amp;gt;(depth(v), length(v))&amp;lt;/math&amp;gt; {{---}} глубина вершины и длина пути до нее из корня поддерева), расстояние до которых от корня их поддерева не превышает &amp;lt;math&amp;gt;min(l, n)&amp;lt;/math&amp;gt;. Тогда просто пройдемся по всем вершинам &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; поддерева &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и прибавим к ответу число вершин в структуре &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt;, таких, что &amp;lt;tex&amp;gt;depth(u) \leqslant l - depth(v)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;length(u) \leqslant l - length(v)&amp;lt;/tex&amp;gt;. Это двумерные запросы, на которые можно отвечать за &amp;lt;math&amp;gt;O(log^2(n)&amp;lt;/math&amp;gt; с помощью [[Многомерное_дерево_отрезков|2d-дерева отрезков]], либо за &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с помощью [[Перечисление_точек_в_произвольном_прямоугольнике_за_n_*_log_%5E(d_-_1)_n_(range_tree)|техники поиска точек в d-мерном пространстве]]. Также читателю предлагается придумать и более эффективные и простые способы решить эту подзадачу.&lt;br /&gt;
&lt;br /&gt;
Оценим итоговую асимптотику : &amp;lt;tex&amp;gt;T(n) = k * T(n / k) + O(n \cdot log(n))&amp;lt;/tex&amp;gt;. Решая это рекурентное соотношение, получим &amp;lt;math&amp;gt;O(n \cdot log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь, как и было обещено, докажем лемму о существовании центроида и опишем алгоритм его эффективного поиска.&lt;br /&gt;
&lt;br /&gt;
=== Лемма о существовании центроида и алгоритм его нахождения. ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
В любом дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; существует центроид.&lt;br /&gt;
|proof=&lt;br /&gt;
Рассмотрим корень дерева &amp;lt;math&amp;gt;(r)&amp;lt;/math&amp;gt;. Положим изначально &amp;lt;math&amp;gt;v = r&amp;lt;/math&amp;gt;. Изначально &amp;lt;math&amp;gt;|subtree(v)| = n&amp;lt;/math&amp;gt;. Среди всех детей &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; выберем вершину &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; с максимальным размером поддерева. Если &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид, то положим &amp;lt;math&amp;gt;v = u&amp;lt;/math&amp;gt; и продолжим выбор нового u, иначе - остановимся. Докажем, что мы в какой-то момент остановимся. Пусть в призвольный момент времени &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; - не центроид и размер её наддерева меньше &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, значит максимальное поддерево имеет размер больше чем &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, т.е. &amp;lt;math&amp;gt;|subtree(u)| &amp;gt; \frac{n}{2}&amp;lt;/math&amp;gt;, а значит размер &amp;quot;наддерева&amp;quot; вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; равен &amp;lt;tex&amp;gt;n - |subtree(u)| &amp;lt; \frac{n}{2}&amp;lt;/tex&amp;gt;. При этом теперь размер любого поддерева, на которое распадется дерево t при удалении вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; не превосходит &amp;lt;math&amp;gt;|subtree(u)| - 1&amp;lt;/math&amp;gt;, т.к. наддерево имеет размер меньше, чем поддерево &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, а любое поддерево вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; имеет хотя бы на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; вершину меньше (сама вершина &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;). По индукции получаем, что в любой момент времени размер наддерева вершины v меньше &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, значит мы будем спускаться только вниз по дереву &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, и при переходе к вершине &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; - сыну &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; размер максимального поддерева уменьшится как минимум на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Значит не более чем за &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; шагов наши действия прекратятся и мы окажемся в центроиде дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
Итак, мы конструктивно доказали существование центроида и привели линейный относительно размера дерева алгоритм его нахождения. &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Реализация ===&lt;br /&gt;
&lt;br /&gt;
Поиск центроида в дереве:&lt;br /&gt;
&lt;br /&gt;
  '''int''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int''' sz[n])&lt;br /&gt;
     max_subtree = -1&lt;br /&gt;
     '''for''' u : children(v)&lt;br /&gt;
          '''if''' sz[u] &amp;gt; sz[v] / 2 and sz[u] &amp;gt; sz[max_subtree] &amp;lt;font color=green&amp;gt;// считаем sz[-1] = 0 &amp;lt;/font &amp;gt;&lt;br /&gt;
               max_subtree = u&lt;br /&gt;
     '''if''' max_subtree == -1&lt;br /&gt;
          return v&lt;br /&gt;
     '''else''' &lt;br /&gt;
          '''return''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz)&lt;br /&gt;
&lt;br /&gt;
Шаблон решения произвольной задачи на статическую центроидную декомпозицию:&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int''' sz[n])&lt;br /&gt;
     c = &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz) &amp;lt;font color=green&amp;gt;// находим c - центроид поддерева вершины v &amp;lt;/font &amp;gt;&lt;br /&gt;
     ch = children[c]&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{deleteEdges}&amp;lt;/tex&amp;gt;(c, children) &amp;lt;font color=green&amp;gt;// удаляем все ребра между вершиной с и детьми, чтобы мы не смогли из детей попасть в с. Это полезно делать, если решение  подзадачи для поддерева предполагает проход dfs &amp;lt;/font &amp;gt;&lt;br /&gt;
     '''for''' c2 : ch[c]&lt;br /&gt;
          &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;(children, c2, sz)&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{mergeSolution}&amp;lt;/tex&amp;gt;(children, c2) &amp;lt;font color=green&amp;gt;// решаем для текущего поддерева &amp;lt;/font &amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Динамическая центроидная декомпозиция (дерево центроидной декомпозиции) ==&lt;br /&gt;
Теперь вернемся ко второй задаче введения. Для ее решения мы пользовались динамичекой версией devide&amp;amp;conqure {{---}} деревом отрезков. В предыдущем пункте мы определили статический оналог devide&amp;amp;conqure для дерева. Теперь обобщим этот метод для динамических задач.&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Деревом центроидов (или центроидной декомпозицией)''' дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; называют дерево &amp;lt;math&amp;gt;T(t)&amp;lt;/math&amp;gt;, построенное на вершинах дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; следующим образом :&lt;br /&gt;
* Пусть вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; - центроид дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Объявим его корнем нового дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пусть при удалении вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; из дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; оно распалось на поддеревья &amp;lt;tex&amp;gt;t_1, t_2,..., t_k&amp;lt;/tex&amp;gt;, тогда детьми вершины c объявим &amp;lt;tex&amp;gt;T(t_1), T(t_t),..., T(t_k)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
=== Свойства центроидной декомпозиции ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
'''Свойства центроидной декомпозиции''' :&lt;br /&gt;
* Глубина дерева центроидов не превосходит &amp;lt;tex&amp;gt;log(n)&amp;lt;/tex&amp;gt;. &lt;br /&gt;
* Каждая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; является центроидом одного из поддеревьев дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;&lt;br /&gt;
* Каждая вершина дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; поддеревьям дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (еще говорят, что вершина принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; центроидам дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, или что эти центроиды содержат вершину)&lt;br /&gt;
* Для любых вершин &amp;lt;tex&amp;gt;u, v \in T (u \neq v)&amp;lt;/tex&amp;gt; верно ровно одно из следующих трех утверждений : &lt;br /&gt;
a) &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
b) &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
c) &amp;lt;tex&amp;gt;T(u) \cap T(v) = \emptyset &amp;lt;/tex&amp;gt;&lt;br /&gt;
* Простой путь между любой парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; содержит центроид &amp;lt;tex&amp;gt;c \in T(t)&amp;lt;/tex&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
* Действительно, т.к. размер поддерева &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; не превосходит &amp;lt;tex&amp;gt;\frac{|subtree(c)|}{2}&amp;lt;/tex&amp;gt;, то при спуске в каждую следующую вершину на пути к любому листу в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; размер поддерева вершины, в которой мы сейчас находимся, уменьшается как минимум на &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;. Значит длина всего пути до листа не превосходит &amp;lt;math&amp;gt;log(n)&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
* Второе свойство очевидно из построения дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, т.к. если вершина принадлежит дереву центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то она является центроидом, а из построения &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы знаем, что каждая вершина исходного дерева принадлежит и дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Третье свойство - прямое следствие первых двух, т.к. вершина принадлежит любому центроиду &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; т.и т.т., когда c - отец вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов. Т.к. вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; точно принадлежит дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (свойство 2), то она лежит на каком-то пути в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, причем все ее родители (центроиды) ее содержат. А по свойству 1 длина любого вертикального (и даже простого) пути есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
* Четвертое свойство очевидно из того, что &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; - дерево. Т.к. &amp;lt;math&amp;gt;T(u)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;T(v)&amp;lt;/math&amp;gt; - поддеревья различных вершин дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то либо они не пересекаются, либо &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; - предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;, либо &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; - предок &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
* Для доказательства последнего свойства выберем в качестве вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;lca(u, v)&amp;lt;/math&amp;gt; в дереве центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Покажем, что так выбранная вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удовлетворяет заявленным свойствам. То, что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt; - очевидно по определению &amp;lt;math&amp;gt;lca&amp;lt;/math&amp;gt;, т.к. каждый предок любой вершины в дереве центроидов содержит эту вершину. Теперь докажем, что &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежит на пути между парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt;. Т.к. &amp;lt;math&amp;gt;c = lca(u, v)&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то из &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; нет ребра в такого сына, который содержит одновременно &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в своем поддереве (в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;), значит после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; разделится на несколько поддеревьев, таких что вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; окажутся в разных компонентах связности. А значит найдется такое ребро &amp;lt;math&amp;gt;(c, x)&amp;lt;/math&amp;gt;, которое принадлежало пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, но после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удалилось. Это доказывает то, что вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежала на пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
С помощью описанных свойств дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы можем решить задачу 2 для дерева :&lt;br /&gt;
&lt;br /&gt;
=== Пример решения задачи с помощью центроидной декомпозиции === &lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин. В каждый момент времени любая вершина дерева может быть либо помечена, либо нет. Изначально помечена только вершина номер &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Дан список из &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; запросов : &lt;br /&gt;
* Вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; пометили.&lt;br /&gt;
* С вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; сняли пометку.&lt;br /&gt;
* Найти номер ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Решение :&lt;br /&gt;
&lt;br /&gt;
Построим центроидную декомпозицию &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Изначально посчитаем для каждого центроида, содержащего вершину &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt; посчитаем расстояние до вершины &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Для этого воспользуемся [[Метод_двоичного_подъема|методом двоичных подъемов для поиска lca пары вершин в дереве]], а также тем фактом, что если глубина &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве определена как расстояние от корня до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то длина пути между парой вершин &amp;lt;math&amp;gt;(u, v)&amp;lt;/math&amp;gt; есть &amp;lt;tex&amp;gt;len(u, v) = h(u) + h(v) - 2 \cdot h(lca(u, v))&amp;lt;/tex&amp;gt;. Если изначально предпосчитать проходом [[Обход_в_глубину,_цвета_вершин| dfs]] величины &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, то ответ на запрос &amp;lt;math&amp;gt;len(u, v)&amp;lt;/math&amp;gt; можно делать за время &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с &amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt;доп. памятью. Также можно воспользоваться техникой [[Сведение_задачи_LCA_к_задаче_RMQ|сведения задачи LCA к RMQ]] и решить с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; времени на запрос. Теперь научимся отвечать на запросы. Из последнего свойства центроидной декомпозиции видно, что если &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; - искомая ближайшая помеченная вершина к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то путь между ними содержит центроид &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;, причем &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; - предок одновременно вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве&amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Поэтому заведем [[Красно-черное_дерево|двоичное дерево поиска]] для каждого центроида &amp;lt;math&amp;gt;c \in T&amp;lt;/math&amp;gt;. В этой структуре для каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; будем хранить пары &amp;lt;math&amp;gt;(len(c, u), u)&amp;lt;/math&amp;gt; для всех помеченных вершин &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в поддереве центроидов &amp;lt;math&amp;gt;T(c)&amp;lt;/math&amp;gt;. Когда приходит запрос пометить вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; - добавим в структуру данных для всех предков &amp;lt;math&amp;gt;p_c&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; пары &amp;lt;math&amp;gt;(len(p_c, v), v)&amp;lt;/math&amp;gt;. Мы совершим &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; добавлений, затратив &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; действий на каждое. Запрос снятия пометки с вершины обрабатывается аналогичными удалениями. Запрос поиска ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины - это запрос поиска вершины u, такой что  величина &amp;lt;math&amp;gt;len(c, u) + len(c, v)&amp;lt;/math&amp;gt; минимальна, где &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; - предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов (по пятому свойству, нас интересуют именно такие &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;). Этот запрос занимает так же &amp;lt;tex&amp;gt;O(log^2(n))&amp;lt;/tex&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
Итак, мы научились решать задачу с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и временной сложностью &amp;lt;math&amp;gt;O(log^2(n))&amp;lt;/math&amp;gt; на запрос любого типа с предварительным предпосчетом за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Варианты хранения центроидной декомпозиции ==&lt;br /&gt;
Для хранения дерева центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; есть &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; подхода, имеющих свои преимущества и недостатки :&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева запомним величину &amp;lt;math&amp;gt;p_v&amp;lt;/math&amp;gt; - номер предка вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Этот подход наиболее экономный по памяти (&amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;), но уступает в скорости и функциональности.&lt;br /&gt;
&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева будем хранить весь массив предков &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt; в дереве центроидов.&lt;br /&gt;
&lt;br /&gt;
Этот подход уступает в количестве необходимой дополнительной памяти (&amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt; суммарно), но имеет ряд преимуществ :&lt;br /&gt;
&lt;br /&gt;
1) При проходе по массиву предков фиксированной вершины будет выигрыш в скорости работы, т.к. весь массив будет лежать непрерывным блоком данных и следовательно будет закэширован&lt;br /&gt;
&lt;br /&gt;
2) На массиве предков можно строить различные структуры данных (такие как, например, дерево отрезков) для быстрого (в случае с деревом отрезков &amp;lt;math&amp;gt;O(log(log(n)))&amp;lt;/math&amp;gt; на запрос) поиска предка с необходимыми свойствами. Так, например, в описанной выше задаче про помеченные вершины наибольшего общего предка можно искать методом двоичных подъемов за &amp;lt;math&amp;gt;O(log(log(n))&amp;lt;/math&amp;gt; на запрос, т.к. размер массива предков есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; (по свойству 1 центроидной декомпозиции). Используя эту оптимизацию можно получить время &amp;lt;math&amp;gt;O(log(n)) + O(log(log(n)) = O(log(n))&amp;lt;/math&amp;gt; на запрос нахождения ближайшей помеченной вершины. Чтобы добиться улучшенной асимптотики для запросов изменения можно хранить дерево отрезков на каждом из путей &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt;, в каждой вершине которого хранить двоичное дерево поиска и поддерживать отложенные операции. Тогда ответ на эти запросы будет занимать &amp;lt;math&amp;gt;O(log(n) \cdot log(log(n))&amp;lt;/math&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
&lt;br /&gt;
*[[:Дерево_отрезков._Построение|Дерево отрезков]]&lt;br /&gt;
*[[:Heavy-light_декомпозиция|Heavy-light декомпозиция]]&lt;br /&gt;
*[[:Метод_двоичного_подъема| Метод двоичного подъема]]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы и структуры данных]]&lt;/div&gt;</summary>
		<author><name>92.255.113.52</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61620</id>
		<title>Centroid decomposition</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61620"/>
				<updated>2017-06-14T21:24:46Z</updated>
		
		<summary type="html">&lt;p&gt;92.255.113.52: /* Лемма о существовании центроида и алгоритм его нахождения. */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Centroid decomposition (рус. центроидная декомпозиция) {{---}} это структура данных, позволяющая отвечать на запросы на дереве. Чаще всего это запросы, связанные с нахождением функции на вершинах, связанных неравенством на расстояние между ними в дереве. Также иногда применяется для запросов на путях в дереве.&lt;br /&gt;
&lt;br /&gt;
== Введение ==&lt;br /&gt;
Рассмотрим &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; задачи на обычном массиве (в дальнейшем мы будем их обобщать на случай дерева):&lt;br /&gt;
&lt;br /&gt;
Задача &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть массив &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; положительных целых чисел из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов и числа &amp;lt;tex&amp;gt;W \geqslant 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; индексов массива, таких что &amp;lt;tex&amp;gt;|j - i| \leqslant l &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\sum_{i=0}^{n - 1} a_i \leqslant W&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
Задача &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть прямая дорога, на которой расположены &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; городов. В некоторых городах есть госпитали, которые могут принимать больных. Поступают запросы вида :&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;, в котором находится больной и требуется найти такой город &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;|u - v|&amp;lt;/tex&amp;gt; минимально возможное.&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что больше он не будет принимать больных&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что теперь он может принимать больных&lt;br /&gt;
}}&lt;br /&gt;
Для начала решим обе задачи. &lt;br /&gt;
Первая задача решается методом [[Сортировка_слиянием|qevide&amp;amp;conqure (рус. разделяй и властвуй)]] {{---}} давайте разделим массив &amp;lt;tex&amp;gt;a[0...n-1]&amp;lt;/tex&amp;gt; на 2 массива &amp;lt;tex&amp;gt;a[0...\frac{n}{2} - 1]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;a[\frac{n}{2}...n-1]&amp;lt;/tex&amp;gt; и рекурсивно решим задачу для каждого из них. Осталось научиться находить количество искомых пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt;, таких что &amp;lt;tex&amp;gt;i &amp;lt; \frac{n}{2}, j \geqslant \frac{n}{2}&amp;lt;/tex&amp;gt;. Для этого воспользуемся другой известной техникой {{---}} методом двух указателей. Посчитаем массив префиксных сумм для правой половины &amp;lt;tex&amp;gt;pref[i] = \sum_{j=\frac{n}{2}}^{i} a_j&amp;lt;/tex&amp;gt; и суффиксных (&amp;lt;tex&amp;gt;suf[i] = \sum_{j=i}^{\frac{n}{2} + 1} a_j&amp;lt;/tex&amp;gt;) {{---}} для левой. Заведем два указателя (&amp;lt;tex&amp;gt;p_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;p_2&amp;lt;/tex&amp;gt;). Изначально установим &amp;lt;tex&amp;gt;p_1 = \frac{n}{2} - l + 1, p_2 = \frac{n}{2}&amp;lt;/tex&amp;gt;. Пока &amp;lt;tex&amp;gt;p_2 - 1&amp;gt; \frac{n}{2}&amp;lt;/tex&amp;gt; и&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;pref[p_2] + suf[p_1] &amp;gt; W &amp;lt;/tex&amp;gt; будем уменьшать &amp;lt;math&amp;gt;p_2&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Если после этого &amp;lt;math&amp;gt;pref[p_2] + suf[p_1] \leqslant W&amp;lt;/math&amp;gt;, то к ответу прибавим &amp;lt;math&amp;gt;(p_2 - \frac{n}{2} + 1) * (\frac{n}{2} - p_1)&amp;lt;/math&amp;gt;, посго, увеличим &amp;lt;math&amp;gt;p_1&amp;lt;/math&amp;gt; на &amp;lt;math/math&amp;gt;. Так будем делать, пока &amp;lt;math&amp;gt;p_1 &amp;lt; \frac{n}{2}&amp;lt;/math&amp;gt;. В конце сложим текущий ответ и ответы для половин массива {{---}} получим ответ на задачу. Асимптотика такого алгоритма : &amp;lt;tex&amp;gt;T(n) = 2 * T(n / 2) + O(n) = O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вторая задача имеет запросы на изменение и поэтому надо применить динамическую версию qevide&amp;amp;conqure {{---}} [[Дерево_отрезков._Построение|дерево отрезков]]. Построим дерево отрезков,&lt;br /&gt;
поддерживающее 2 вида запросов : присвоение в точке и минимум на отрезке. Изначально сделаем так, чтобы дереву отрезков соответствовал массив &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;b_i = 0&amp;lt;/tex&amp;gt;, если в i-м городе принимает госпиталь и &amp;lt;tex&amp;gt;b_i = 1&amp;lt;/tex&amp;gt; иначе. Когда в каком-то городе открывается/закрывается госпиталь {{---}} делаем запрос на изменение в дереве отрезков. Когда требуется узнать ближайщий госпиталь к &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-му городу, можно воспользоваться одной из следующих идей : &lt;br /&gt;
а) (&amp;lt;tex&amp;gt;O(n \cdot log^2(n)&amp;lt;/tex&amp;gt;) Бинарным поиском ищем ближайший слева и ближайший справа к i-му городу госпиталь (такой город &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, что &amp;lt;tex&amp;gt;\min\limits_{k=i..j}b_k= 1&amp;lt;/tex&amp;gt;). Для этого внутри бинарного поиска каждый раз делаем запрос на поиск минимума в дереве отрезков.  &lt;br /&gt;
б) (&amp;lt;tex&amp;gt;O(n \cdot log(n)&amp;lt;/tex&amp;gt;) Будем одним спуском/подъемом по дереву определять, куда нам нужно идти (в левое или правое поддерево), тем самым делая одновоременно и бинарный поиск, и спуск/подъем по дереву.&lt;br /&gt;
&lt;br /&gt;
== Статическая центроидная декомпозиция ==&lt;br /&gt;
Перейдем к обобщению поставленных задач на случай дерева. Начнем, как и полагается, с первой:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть взвешенное дерево &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин, в каждой вершине которого написаны положительные целые числа. Также по-прежнему даны числа &amp;lt;tex&amp;gt;W &amp;gt;= 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; вершин дерева, таких что расстояние между ними не превосходит &amp;lt;math&amp;gt;l&amp;lt;/math&amp;gt; по числу ребер и не превосходит &amp;lt;math&amp;gt;W&amp;lt;/math&amp;gt; по сумме весов.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Для решения новой задачи применим ту же идею, что и была до этого {{---}} разделяй и властвуй. Для этого нам потребуется следующий объект :&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|id=191213&lt;br /&gt;
|definition =&lt;br /&gt;
'''Центроидом дерева''' (англ. ''centroid'') называется такая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, после удаления которой дерево разбивается на несколько (&amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;) поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,..., t_k&amp;lt;/tex&amp;gt;, таких что для каждого &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; : &amp;lt;tex&amp;gt;|t_i| \leqslant \frac{n}{2}&amp;lt;/tex&amp;gt;, т.е. размер каждого поддерева не превосходит половины размера исходного дерева.&lt;br /&gt;
}}&lt;br /&gt;
Итак, в случае дерева идея разделяй-и-властвуй из предыдущего пункта будет формулироваться так : найдем центроид (доказательство её существования и алгоритм нахождение см. далее). Предположим, что мы сумели найти центроид за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; {{---}} размер дерева. Тогда, как и в упрощенной версии задачи {{---}} рекурсивно найдем ответ для всех поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,..., t_k&amp;lt;/tex&amp;gt;, после чего попытаемся найти недостающие пары вершин, находящиехя в разных поддеревьях и удовлетворяющих вопросу задачи. Для этого будем отвечать на следующие запросы : пусть мы сейчас считаем все пары, где первая из вершин находится в поддереве &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и мы в некоторой структуре данных &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; храним все вершины остальных деревьев (каждую вершину задаем парой &amp;lt;math&amp;gt;(depth(v), length(v))&amp;lt;/math&amp;gt; {{---}} глубина вершины и длина пути до нее из корня поддерева), расстояние до которых от корня их поддерева не превышает &amp;lt;math&amp;gt;min(l, n)&amp;lt;/math&amp;gt;. Тогда просто пройдемся по всем вершинам &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; поддерева &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и прибавим к ответу число вершин в структуре &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt;, таких, что &amp;lt;tex&amp;gt;depth(u) \leqslant l - depth(v)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;length(u) \leqslant l - length(v)&amp;lt;/tex&amp;gt;. Это двумерные запросы, на которые можно отвечать за &amp;lt;math&amp;gt;O(log^2(n)&amp;lt;/math&amp;gt; с помощью [[Многомерное_дерево_отрезков|2d-дерева отрезков]], либо за &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с помощью [[Перечисление_точек_в_произвольном_прямоугольнике_за_n_*_log_%5E(d_-_1)_n_(range_tree)|техники поиска точек в d-мерном пространстве]]. Также читателю предлагается придумать и более эффективные и простые способы решить эту подзадачу.&lt;br /&gt;
&lt;br /&gt;
Оценим итоговую асимптотику : &amp;lt;tex&amp;gt;T(n) = k * T(n / k) + O(n \cdot log(n))&amp;lt;/tex&amp;gt;. Решая это рекурентное соотношение, получим &amp;lt;math&amp;gt;O(n \cdot log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь, как и было обещено, докажем лемму о существовании центроида и опишем алгоритм его эффективного поиска.&lt;br /&gt;
&lt;br /&gt;
=== Лемма о существовании центроида и алгоритм его нахождения. ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
В любом дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; существует центроид.&lt;br /&gt;
|proof=&lt;br /&gt;
Рассмотрим корень дерева &amp;lt;math&amp;gt;(r)&amp;lt;/math&amp;gt;. Положим изначально &amp;lt;math&amp;gt;v = r&amp;lt;/math&amp;gt;. Изначально &amp;lt;math&amp;gt;|subtree(v)| = n&amp;lt;/math&amp;gt;. Среди всех детей &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; выберем вершину &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; с максимальным размером поддерева. Если &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид, то положим &amp;lt;math&amp;gt;v = u&amp;lt;/math&amp;gt; и продолжим выбор нового u, иначе - остановимся. Докажем, что мы в какой-то момент остановимся. Пусть в призвольный момент времени &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; - не центроид и размер её наддерева меньше &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, значит максимальное поддерево имеет размер больше чем &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, т.е. &amp;lt;math&amp;gt;|subtree(u)| &amp;gt; \frac{n}{2}&amp;lt;/math&amp;gt;, а значит размер &amp;quot;наддерева&amp;quot; вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; равен &amp;lt;tex&amp;gt;n - |subtree(u)| &amp;lt; \frac{n}{2}&amp;lt;/tex&amp;gt;. При этом теперь размер любого поддерева, на которое распадется дерево t при удалении вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; не превосходит &amp;lt;math&amp;gt;|subtree(u)| - 1&amp;lt;/math&amp;gt;, т.к. наддерево имеет размер меньше, чем поддерево &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, а любое поддерево вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; имеет хотя бы на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; вершину меньше (сама вершина &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;). По индукции получаем, что в любой момент времени размер наддерева вершины v меньше &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, значит мы будем спускаться только вниз по дереву &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, и при переходе к вершине &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; - сыну &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; размер максимального поддерева уменьшится как минимум на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Значит не более чем за &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; шагов наши действия прекратятся и мы окажемся в центроиде дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
Итак, мы конструктивно доказали существование центроида и привели линейный относительно размера дерева алгоритм его нахождения. &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Реализация ===&lt;br /&gt;
&lt;br /&gt;
Поиск центроида в дереве:&lt;br /&gt;
&lt;br /&gt;
  '''int''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int''' sz[n])&lt;br /&gt;
     max_subtree = -1&lt;br /&gt;
     '''for''' u : children(v)&lt;br /&gt;
          '''if''' sz[u] &amp;gt; sz[v] / 2 and sz[u] &amp;gt; sz[max_subtree] &amp;lt;font color=green&amp;gt;// считаем sz[-1] = 0 &amp;lt;/font &amp;gt;&lt;br /&gt;
               max_subtree = u&lt;br /&gt;
     '''if''' max_subtree == -1&lt;br /&gt;
          return v&lt;br /&gt;
     '''else''' &lt;br /&gt;
          '''return''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz)&lt;br /&gt;
&lt;br /&gt;
Шаблон решения произвольной задачи на статическую центроидную декомпозицию:&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int''' sz[n])&lt;br /&gt;
     c = &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz) &amp;lt;font color=green&amp;gt;// находим c - центроид поддерева вершины v &amp;lt;/font &amp;gt;&lt;br /&gt;
     ch = children[c]&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{deleteEdges}&amp;lt;/tex&amp;gt;(c, children) &amp;lt;font color=green&amp;gt;// удаляем все ребра между вершиной с и детьми, чтобы мы не смогли из детей попасть в с. Это полезно делать, если решение  подзадачи для поддерева предполагает проход dfs &amp;lt;/font &amp;gt;&lt;br /&gt;
     '''for''' c2 : ch[c]&lt;br /&gt;
          &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;(children, c2, sz)&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{mergeSolution}&amp;lt;/tex&amp;gt;(children, c2) &amp;lt;font color=green&amp;gt;// решаем для текущего поддерева &amp;lt;/font &amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Динамическая центроидная декомпозиция (дерево центроидной декомпозиции) ==&lt;br /&gt;
Теперь вернемся ко второй задаче введения. Для ее решения мы пользовались динамичекой версией devide&amp;amp;conqure {{---}} деревом отрезков. В предыдущем пункте мы определили статический оналог devide&amp;amp;conqure для дерева. Теперь обобщим этот метод для динамических задач.&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Деревом центроидов (или центроидной декомпозицией)''' дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; называют дерево &amp;lt;math&amp;gt;T(t)&amp;lt;/math&amp;gt;, построенное на вершинах дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; следующим образом :&lt;br /&gt;
* Пусть вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; - центроид дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Объявим его корнем нового дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пусть при удалении вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; из дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; оно распалось на поддеревья &amp;lt;tex&amp;gt;t_1, t_2,..., t_k&amp;lt;/tex&amp;gt;, тогда детьми вершины c объявим &amp;lt;tex&amp;gt;T(t_1), T(t_t),..., T(t_k)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
=== Свойства центроидной декомпозиции ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
'''Свойства центроидной декомпозиции''' :&lt;br /&gt;
* Глубина дерева центроидов не превосходит &amp;lt;tex&amp;gt;log(n)&amp;lt;/tex&amp;gt;. &lt;br /&gt;
* Каждая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; является центроидом одного из поддеревьев дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;&lt;br /&gt;
* Каждая вершина дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; поддеревьям дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (еще говорят, что вершина принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; центроидам дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, или что эти центроиды содержат вершину)&lt;br /&gt;
* Для любых вершин &amp;lt;tex&amp;gt;u, v \in T (u \neq v)&amp;lt;/tex&amp;gt; верно ровно одно из следующих трех утверждений : &lt;br /&gt;
a) &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
b) &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
c) &amp;lt;tex&amp;gt;T(u) \cap T(v) = \emptyset &amp;lt;/tex&amp;gt;&lt;br /&gt;
* Простой путь между любой парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; содержит центроид &amp;lt;tex&amp;gt;c \in T(t)&amp;lt;/tex&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
* Действительно, т.к. размер поддерева &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; не превосходит &amp;lt;tex&amp;gt;\frac{|subtree(c)|}{2}&amp;lt;/tex&amp;gt;, то при спуске в каждую следующую вершину на пути к любому листу в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; размер поддерева вершины, в которой мы сейчас находимся, уменьшается как минимум на &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;. Значит длина всего пути до листа не превосходит &amp;lt;math&amp;gt;log(n)&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
* Второе свойство очевидно из построения дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, т.к. если вершина принадлежит дереву центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то она является центроидом, а из построения &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы знаем, что каждая вершина исходного дерева принадлежит и дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Третье свойство - прямое следствие первых двух, т.к. вершина принадлежит любому центроиду &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; т.и т.т., когда c - отец вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов. Т.к. вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; точно принадлежит дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (свойство 2), то она лежит на каком-то пути в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, причем все ее родители (центроиды) ее содержат. А по свойству 1 длина любого вертикального (и даже простого) пути есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
* Четвертое свойство очевидно из того, что &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; - дерево. Т.к. &amp;lt;math&amp;gt;T(u)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;T(v)&amp;lt;/math&amp;gt; - поддеревья различных вершин дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то либо они не пересекаются, либо &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; - предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;, либо &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; - предок &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
* Для доказательства последнего свойства выберем в качестве вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;lca(u, v)&amp;lt;/math&amp;gt; в дереве центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Покажем, что так выбранная вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удовлетворяет заявленным свойствам. То, что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt; - очевидно по определению &amp;lt;math&amp;gt;lca&amp;lt;/math&amp;gt;, т.к. каждый предок любой вершины в дереве центроидов содержит эту вершину. Теперь докажем, что &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежит на пути между парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt;. Т.к. &amp;lt;math&amp;gt;c = lca(u, v)&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то из &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; нет ребра в такого сына, который содержит одновременно &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в своем поддереве (в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;), значит после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; разделится на несколько поддеревьев, таких что вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; окажутся в разных компонентах связности. А значит найдется такое ребро &amp;lt;math&amp;gt;(c, x)&amp;lt;/math&amp;gt;, которое принадлежало пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, но после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удалилось. Это доказывает то, что вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежала на пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
С помощью описанных свойств дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы можем решить задачу 2 для дерева :&lt;br /&gt;
&lt;br /&gt;
=== Пример решения задачи с помощью центроидной декомпозиции === &lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин. В каждый момент времени любая вершина дерева может быть либо помечена, либо нет. Изначально помечена только вершина номер 0. Дан список из &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; запросов : &lt;br /&gt;
* Вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; пометили.&lt;br /&gt;
* С вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; сняли пометку.&lt;br /&gt;
* Найти номер ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Решение :&lt;br /&gt;
&lt;br /&gt;
Построим центроидную декомпозицию &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Изначально посчитаем для каждого центроида, содержащего вершину &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt; посчитаем расстояние до вершины &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Для этого воспользуемся [[Метод_двоичного_подъема|методом двоичных подъемов для поиска lca пары вершин в дереве]], а также тем фактом, что если глубина &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве определена как расстояние от корня до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то длина пути между парой вершин &amp;lt;math&amp;gt;(u, v)&amp;lt;/math&amp;gt; есть &amp;lt;tex&amp;gt;len(u, v) = h(u) + h(v) - 2 \cdot h(lca(u, v))&amp;lt;/tex&amp;gt;. Если изначально предпосчитать проходом [[Обход_в_глубину,_цвета_вершин| dfs]] величины &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, то ответ на запрос &amp;lt;math&amp;gt;len(u, v)&amp;lt;/math&amp;gt; можно делать за время &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с &amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt;доп. памятью. Также можно воспользоваться техникой [[Сведение_задачи_LCA_к_задаче_RMQ|сведения задачи LCA к RMQ]] и решить с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; времени на запрос. Теперь научимся отвечать на запросы. Из последнего свойства центроидной декомпозиции видно, что если &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; - искомая ближайшая помеченная вершина к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то путь между ними содержит центроид &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;, причем &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; - предок одновременно вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве&amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Поэтому заведем [[Красно-черное_дерево|двоичное дерево поиска]] для каждого центроида &amp;lt;math&amp;gt;c \in T&amp;lt;/math&amp;gt;. В этой структуре для каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; будем хранить пары &amp;lt;math&amp;gt;(len(c, u), u)&amp;lt;/math&amp;gt; для всех помеченных вершин &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в поддереве центроидов &amp;lt;math&amp;gt;T(c)&amp;lt;/math&amp;gt;. Когда приходит запрос пометить вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; - добавим в структуру данных для всех предков &amp;lt;math&amp;gt;p_c&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; пары &amp;lt;math&amp;gt;(len(p_c, v), v)&amp;lt;/math&amp;gt;. Мы совершим &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; добавлений, затратив &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; действий на каждое. Запрос снятия пометки с вершины обрабатывается аналогичными удалениями. Запрос поиска ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины - это запрос поиска вершины u, такой что  величина &amp;lt;math&amp;gt;len(c, u) + len(c, v)&amp;lt;/math&amp;gt; минимальна, где &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; - предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов (по пятому свойству, нас интересуют именно такие &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;). Этот запрос занимает так же &amp;lt;tex&amp;gt;O(log^2(n))&amp;lt;/tex&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
Итак, мы научились решать задачу с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и временной сложностью &amp;lt;math&amp;gt;O(log^2(n))&amp;lt;/math&amp;gt; на запрос любого типа с предварительным предпосчетом за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Варианты хранения центроидной декомпозиции ==&lt;br /&gt;
Для хранения дерева центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; есть &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; подхода, имеющих свои преимущества и недостатки :&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева запомним величину &amp;lt;math&amp;gt;p_v&amp;lt;/math&amp;gt; - номер предка вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Этот подход наиболее экономный по памяти (&amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;), но уступает в скорости и функциональности.&lt;br /&gt;
&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева будем хранить весь массив предков &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt; в дереве центроидов.&lt;br /&gt;
&lt;br /&gt;
Этот подход уступает в количестве необходимой дополнительной памяти (&amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt; суммарно), но имеет ряд преимуществ :&lt;br /&gt;
&lt;br /&gt;
1) При проходе по массиву предков фиксированной вершины будет выигрыш в скорости работы, т.к. весь массив будет лежать непрерывным блоком данных и следовательно будет закэширован&lt;br /&gt;
&lt;br /&gt;
2) На массиве предков можно строить различные структуры данных (такие как, например, дерево отрезков) для быстрого (в случае с деревом отрезков &amp;lt;math&amp;gt;O(log(log(n)))&amp;lt;/math&amp;gt; на запрос) поиска предка с необходимыми свойствами. Так, например, в описанной выше задаче про помеченные вершины наибольшего общего предка можно искать методом двоичных подъемов за &amp;lt;math&amp;gt;O(log(log(n))&amp;lt;/math&amp;gt; на запрос, т.к. размер массива предков есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; (по свойству 1 центроидной декомпозиции). Используя эту оптимизацию можно получить время &amp;lt;math&amp;gt;O(log(n)) + O(log(log(n)) = O(log(n))&amp;lt;/math&amp;gt; на запрос нахождения ближайшей помеченной вершины. Чтобы добиться улучшенной асимптотики для запросов изменения можно хранить дерево отрезков на каждом из путей &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt;, в каждой вершине которого хранить двоичное дерево поиска и поддерживать отложенные операции. Тогда ответ на эти запросы будет занимать &amp;lt;math&amp;gt;O(log(n) \cdot log(log(n))&amp;lt;/math&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
&lt;br /&gt;
*[[:Дерево_отрезков._Построение|Дерево отрезков]]&lt;br /&gt;
*[[:Heavy-light_декомпозиция|Heavy-light декомпозиция]]&lt;br /&gt;
*[[:Метод_двоичного_подъема| Метод двоичного подъема]]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы и структуры данных]]&lt;/div&gt;</summary>
		<author><name>92.255.113.52</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61619</id>
		<title>Centroid decomposition</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61619"/>
				<updated>2017-06-14T21:24:29Z</updated>
		
		<summary type="html">&lt;p&gt;92.255.113.52: /* Введение */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Centroid decomposition (рус. центроидная декомпозиция) {{---}} это структура данных, позволяющая отвечать на запросы на дереве. Чаще всего это запросы, связанные с нахождением функции на вершинах, связанных неравенством на расстояние между ними в дереве. Также иногда применяется для запросов на путях в дереве.&lt;br /&gt;
&lt;br /&gt;
== Введение ==&lt;br /&gt;
Рассмотрим &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; задачи на обычном массиве (в дальнейшем мы будем их обобщать на случай дерева):&lt;br /&gt;
&lt;br /&gt;
Задача &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть массив &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; положительных целых чисел из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов и числа &amp;lt;tex&amp;gt;W \geqslant 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; индексов массива, таких что &amp;lt;tex&amp;gt;|j - i| \leqslant l &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\sum_{i=0}^{n - 1} a_i \leqslant W&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
Задача &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть прямая дорога, на которой расположены &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; городов. В некоторых городах есть госпитали, которые могут принимать больных. Поступают запросы вида :&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;, в котором находится больной и требуется найти такой город &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;|u - v|&amp;lt;/tex&amp;gt; минимально возможное.&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что больше он не будет принимать больных&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что теперь он может принимать больных&lt;br /&gt;
}}&lt;br /&gt;
Для начала решим обе задачи. &lt;br /&gt;
Первая задача решается методом [[Сортировка_слиянием|qevide&amp;amp;conqure (рус. разделяй и властвуй)]] {{---}} давайте разделим массив &amp;lt;tex&amp;gt;a[0...n-1]&amp;lt;/tex&amp;gt; на 2 массива &amp;lt;tex&amp;gt;a[0...\frac{n}{2} - 1]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;a[\frac{n}{2}...n-1]&amp;lt;/tex&amp;gt; и рекурсивно решим задачу для каждого из них. Осталось научиться находить количество искомых пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt;, таких что &amp;lt;tex&amp;gt;i &amp;lt; \frac{n}{2}, j \geqslant \frac{n}{2}&amp;lt;/tex&amp;gt;. Для этого воспользуемся другой известной техникой {{---}} методом двух указателей. Посчитаем массив префиксных сумм для правой половины &amp;lt;tex&amp;gt;pref[i] = \sum_{j=\frac{n}{2}}^{i} a_j&amp;lt;/tex&amp;gt; и суффиксных (&amp;lt;tex&amp;gt;suf[i] = \sum_{j=i}^{\frac{n}{2} + 1} a_j&amp;lt;/tex&amp;gt;) {{---}} для левой. Заведем два указателя (&amp;lt;tex&amp;gt;p_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;p_2&amp;lt;/tex&amp;gt;). Изначально установим &amp;lt;tex&amp;gt;p_1 = \frac{n}{2} - l + 1, p_2 = \frac{n}{2}&amp;lt;/tex&amp;gt;. Пока &amp;lt;tex&amp;gt;p_2 - 1&amp;gt; \frac{n}{2}&amp;lt;/tex&amp;gt; и&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;pref[p_2] + suf[p_1] &amp;gt; W &amp;lt;/tex&amp;gt; будем уменьшать &amp;lt;math&amp;gt;p_2&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Если после этого &amp;lt;math&amp;gt;pref[p_2] + suf[p_1] \leqslant W&amp;lt;/math&amp;gt;, то к ответу прибавим &amp;lt;math&amp;gt;(p_2 - \frac{n}{2} + 1) * (\frac{n}{2} - p_1)&amp;lt;/math&amp;gt;, посго, увеличим &amp;lt;math&amp;gt;p_1&amp;lt;/math&amp;gt; на &amp;lt;math/math&amp;gt;. Так будем делать, пока &amp;lt;math&amp;gt;p_1 &amp;lt; \frac{n}{2}&amp;lt;/math&amp;gt;. В конце сложим текущий ответ и ответы для половин массива {{---}} получим ответ на задачу. Асимптотика такого алгоритма : &amp;lt;tex&amp;gt;T(n) = 2 * T(n / 2) + O(n) = O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вторая задача имеет запросы на изменение и поэтому надо применить динамическую версию qevide&amp;amp;conqure {{---}} [[Дерево_отрезков._Построение|дерево отрезков]]. Построим дерево отрезков,&lt;br /&gt;
поддерживающее 2 вида запросов : присвоение в точке и минимум на отрезке. Изначально сделаем так, чтобы дереву отрезков соответствовал массив &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;b_i = 0&amp;lt;/tex&amp;gt;, если в i-м городе принимает госпиталь и &amp;lt;tex&amp;gt;b_i = 1&amp;lt;/tex&amp;gt; иначе. Когда в каком-то городе открывается/закрывается госпиталь {{---}} делаем запрос на изменение в дереве отрезков. Когда требуется узнать ближайщий госпиталь к &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-му городу, можно воспользоваться одной из следующих идей : &lt;br /&gt;
а) (&amp;lt;tex&amp;gt;O(n \cdot log^2(n)&amp;lt;/tex&amp;gt;) Бинарным поиском ищем ближайший слева и ближайший справа к i-му городу госпиталь (такой город &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, что &amp;lt;tex&amp;gt;\min\limits_{k=i..j}b_k= 1&amp;lt;/tex&amp;gt;). Для этого внутри бинарного поиска каждый раз делаем запрос на поиск минимума в дереве отрезков.  &lt;br /&gt;
б) (&amp;lt;tex&amp;gt;O(n \cdot log(n)&amp;lt;/tex&amp;gt;) Будем одним спуском/подъемом по дереву определять, куда нам нужно идти (в левое или правое поддерево), тем самым делая одновоременно и бинарный поиск, и спуск/подъем по дереву.&lt;br /&gt;
&lt;br /&gt;
== Статическая центроидная декомпозиция ==&lt;br /&gt;
Перейдем к обобщению поставленных задач на случай дерева. Начнем, как и полагается, с первой:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть взвешенное дерево &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин, в каждой вершине которого написаны положительные целые числа. Также по-прежнему даны числа &amp;lt;tex&amp;gt;W &amp;gt;= 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; вершин дерева, таких что расстояние между ними не превосходит &amp;lt;math&amp;gt;l&amp;lt;/math&amp;gt; по числу ребер и не превосходит &amp;lt;math&amp;gt;W&amp;lt;/math&amp;gt; по сумме весов.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Для решения новой задачи применим ту же идею, что и была до этого {{---}} разделяй и властвуй. Для этого нам потребуется следующий объект :&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|id=191213&lt;br /&gt;
|definition =&lt;br /&gt;
'''Центроидом дерева''' (англ. ''centroid'') называется такая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, после удаления которой дерево разбивается на несколько (&amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;) поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,..., t_k&amp;lt;/tex&amp;gt;, таких что для каждого &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; : &amp;lt;tex&amp;gt;|t_i| \leqslant \frac{n}{2}&amp;lt;/tex&amp;gt;, т.е. размер каждого поддерева не превосходит половины размера исходного дерева.&lt;br /&gt;
}}&lt;br /&gt;
Итак, в случае дерева идея разделяй-и-властвуй из предыдущего пункта будет формулироваться так : найдем центроид (доказательство её существования и алгоритм нахождение см. далее). Предположим, что мы сумели найти центроид за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; {{---}} размер дерева. Тогда, как и в упрощенной версии задачи {{---}} рекурсивно найдем ответ для всех поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,..., t_k&amp;lt;/tex&amp;gt;, после чего попытаемся найти недостающие пары вершин, находящиехя в разных поддеревьях и удовлетворяющих вопросу задачи. Для этого будем отвечать на следующие запросы : пусть мы сейчас считаем все пары, где первая из вершин находится в поддереве &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и мы в некоторой структуре данных &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; храним все вершины остальных деревьев (каждую вершину задаем парой &amp;lt;math&amp;gt;(depth(v), length(v))&amp;lt;/math&amp;gt; {{---}} глубина вершины и длина пути до нее из корня поддерева), расстояние до которых от корня их поддерева не превышает &amp;lt;math&amp;gt;min(l, n)&amp;lt;/math&amp;gt;. Тогда просто пройдемся по всем вершинам &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; поддерева &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и прибавим к ответу число вершин в структуре &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt;, таких, что &amp;lt;tex&amp;gt;depth(u) \leqslant l - depth(v)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;length(u) \leqslant l - length(v)&amp;lt;/tex&amp;gt;. Это двумерные запросы, на которые можно отвечать за &amp;lt;math&amp;gt;O(log^2(n)&amp;lt;/math&amp;gt; с помощью [[Многомерное_дерево_отрезков|2d-дерева отрезков]], либо за &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с помощью [[Перечисление_точек_в_произвольном_прямоугольнике_за_n_*_log_%5E(d_-_1)_n_(range_tree)|техники поиска точек в d-мерном пространстве]]. Также читателю предлагается придумать и более эффективные и простые способы решить эту подзадачу.&lt;br /&gt;
&lt;br /&gt;
Оценим итоговую асимптотику : &amp;lt;tex&amp;gt;T(n) = k * T(n / k) + O(n \cdot log(n))&amp;lt;/tex&amp;gt;. Решая это рекурентное соотношение, получим &amp;lt;math&amp;gt;O(n \cdot log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь, как и было обещено, докажем лемму о существовании центроида и опишем алгоритм его эффективного поиска.&lt;br /&gt;
&lt;br /&gt;
=== Лемма о существовании центроида и алгоритм его нахождения. ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
В любом дереве t существует центроид.&lt;br /&gt;
|proof=&lt;br /&gt;
Рассмотрим корень дерева &amp;lt;math&amp;gt;(r)&amp;lt;/math&amp;gt;. Положим изначально &amp;lt;math&amp;gt;v = r&amp;lt;/math&amp;gt;. Изначально &amp;lt;math&amp;gt;|subtree(v)| = n&amp;lt;/math&amp;gt;. Среди всех детей &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; выберем вершину &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; с максимальным размером поддерева. Если &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид, то положим &amp;lt;math&amp;gt;v = u&amp;lt;/math&amp;gt; и продолжим выбор нового u, иначе - остановимся. Докажем, что мы в какой-то момент остановимся. Пусть в призвольный момент времени &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; - не центроид и размер её наддерева меньше &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, значит максимальное поддерево имеет размер больше чем &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, т.е. &amp;lt;math&amp;gt;|subtree(u)| &amp;gt; \frac{n}{2}&amp;lt;/math&amp;gt;, а значит размер &amp;quot;наддерева&amp;quot; вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; равен &amp;lt;tex&amp;gt;n - |subtree(u)| &amp;lt; \frac{n}{2}&amp;lt;/tex&amp;gt;. При этом теперь размер любого поддерева, на которое распадется дерево t при удалении вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; не превосходит &amp;lt;math&amp;gt;|subtree(u)| - 1&amp;lt;/math&amp;gt;, т.к. наддерево имеет размер меньше, чем поддерево &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, а любое поддерево вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; имеет хотя бы на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; вершину меньше (сама вершина &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;). По индукции получаем, что в любой момент времени размер наддерева вершины v меньше &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, значит мы будем спускаться только вниз по дереву &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, и при переходе к вершине &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; - сыну &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; размер максимального поддерева уменьшится как минимум на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Значит не более чем за &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; шагов наши действия прекратятся и мы окажемся в центроиде дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
Итак, мы конструктивно доказали существование центроида и привели линейный относительно размера дерева алгоритм его нахождения. &lt;br /&gt;
}}&lt;br /&gt;
=== Реализация ===&lt;br /&gt;
&lt;br /&gt;
Поиск центроида в дереве:&lt;br /&gt;
&lt;br /&gt;
  '''int''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int''' sz[n])&lt;br /&gt;
     max_subtree = -1&lt;br /&gt;
     '''for''' u : children(v)&lt;br /&gt;
          '''if''' sz[u] &amp;gt; sz[v] / 2 and sz[u] &amp;gt; sz[max_subtree] &amp;lt;font color=green&amp;gt;// считаем sz[-1] = 0 &amp;lt;/font &amp;gt;&lt;br /&gt;
               max_subtree = u&lt;br /&gt;
     '''if''' max_subtree == -1&lt;br /&gt;
          return v&lt;br /&gt;
     '''else''' &lt;br /&gt;
          '''return''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz)&lt;br /&gt;
&lt;br /&gt;
Шаблон решения произвольной задачи на статическую центроидную декомпозицию:&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int''' sz[n])&lt;br /&gt;
     c = &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz) &amp;lt;font color=green&amp;gt;// находим c - центроид поддерева вершины v &amp;lt;/font &amp;gt;&lt;br /&gt;
     ch = children[c]&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{deleteEdges}&amp;lt;/tex&amp;gt;(c, children) &amp;lt;font color=green&amp;gt;// удаляем все ребра между вершиной с и детьми, чтобы мы не смогли из детей попасть в с. Это полезно делать, если решение  подзадачи для поддерева предполагает проход dfs &amp;lt;/font &amp;gt;&lt;br /&gt;
     '''for''' c2 : ch[c]&lt;br /&gt;
          &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;(children, c2, sz)&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{mergeSolution}&amp;lt;/tex&amp;gt;(children, c2) &amp;lt;font color=green&amp;gt;// решаем для текущего поддерева &amp;lt;/font &amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Динамическая центроидная декомпозиция (дерево центроидной декомпозиции) ==&lt;br /&gt;
Теперь вернемся ко второй задаче введения. Для ее решения мы пользовались динамичекой версией devide&amp;amp;conqure {{---}} деревом отрезков. В предыдущем пункте мы определили статический оналог devide&amp;amp;conqure для дерева. Теперь обобщим этот метод для динамических задач.&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Деревом центроидов (или центроидной декомпозицией)''' дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; называют дерево &amp;lt;math&amp;gt;T(t)&amp;lt;/math&amp;gt;, построенное на вершинах дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; следующим образом :&lt;br /&gt;
* Пусть вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; - центроид дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Объявим его корнем нового дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пусть при удалении вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; из дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; оно распалось на поддеревья &amp;lt;tex&amp;gt;t_1, t_2,..., t_k&amp;lt;/tex&amp;gt;, тогда детьми вершины c объявим &amp;lt;tex&amp;gt;T(t_1), T(t_t),..., T(t_k)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
=== Свойства центроидной декомпозиции ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
'''Свойства центроидной декомпозиции''' :&lt;br /&gt;
* Глубина дерева центроидов не превосходит &amp;lt;tex&amp;gt;log(n)&amp;lt;/tex&amp;gt;. &lt;br /&gt;
* Каждая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; является центроидом одного из поддеревьев дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;&lt;br /&gt;
* Каждая вершина дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; поддеревьям дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (еще говорят, что вершина принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; центроидам дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, или что эти центроиды содержат вершину)&lt;br /&gt;
* Для любых вершин &amp;lt;tex&amp;gt;u, v \in T (u \neq v)&amp;lt;/tex&amp;gt; верно ровно одно из следующих трех утверждений : &lt;br /&gt;
a) &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
b) &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
c) &amp;lt;tex&amp;gt;T(u) \cap T(v) = \emptyset &amp;lt;/tex&amp;gt;&lt;br /&gt;
* Простой путь между любой парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; содержит центроид &amp;lt;tex&amp;gt;c \in T(t)&amp;lt;/tex&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
* Действительно, т.к. размер поддерева &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; не превосходит &amp;lt;tex&amp;gt;\frac{|subtree(c)|}{2}&amp;lt;/tex&amp;gt;, то при спуске в каждую следующую вершину на пути к любому листу в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; размер поддерева вершины, в которой мы сейчас находимся, уменьшается как минимум на &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;. Значит длина всего пути до листа не превосходит &amp;lt;math&amp;gt;log(n)&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
* Второе свойство очевидно из построения дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, т.к. если вершина принадлежит дереву центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то она является центроидом, а из построения &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы знаем, что каждая вершина исходного дерева принадлежит и дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Третье свойство - прямое следствие первых двух, т.к. вершина принадлежит любому центроиду &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; т.и т.т., когда c - отец вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов. Т.к. вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; точно принадлежит дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (свойство 2), то она лежит на каком-то пути в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, причем все ее родители (центроиды) ее содержат. А по свойству 1 длина любого вертикального (и даже простого) пути есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
* Четвертое свойство очевидно из того, что &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; - дерево. Т.к. &amp;lt;math&amp;gt;T(u)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;T(v)&amp;lt;/math&amp;gt; - поддеревья различных вершин дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то либо они не пересекаются, либо &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; - предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;, либо &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; - предок &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
* Для доказательства последнего свойства выберем в качестве вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;lca(u, v)&amp;lt;/math&amp;gt; в дереве центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Покажем, что так выбранная вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удовлетворяет заявленным свойствам. То, что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt; - очевидно по определению &amp;lt;math&amp;gt;lca&amp;lt;/math&amp;gt;, т.к. каждый предок любой вершины в дереве центроидов содержит эту вершину. Теперь докажем, что &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежит на пути между парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt;. Т.к. &amp;lt;math&amp;gt;c = lca(u, v)&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то из &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; нет ребра в такого сына, который содержит одновременно &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в своем поддереве (в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;), значит после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; разделится на несколько поддеревьев, таких что вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; окажутся в разных компонентах связности. А значит найдется такое ребро &amp;lt;math&amp;gt;(c, x)&amp;lt;/math&amp;gt;, которое принадлежало пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, но после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удалилось. Это доказывает то, что вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежала на пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
С помощью описанных свойств дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы можем решить задачу 2 для дерева :&lt;br /&gt;
&lt;br /&gt;
=== Пример решения задачи с помощью центроидной декомпозиции === &lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин. В каждый момент времени любая вершина дерева может быть либо помечена, либо нет. Изначально помечена только вершина номер 0. Дан список из &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; запросов : &lt;br /&gt;
* Вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; пометили.&lt;br /&gt;
* С вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; сняли пометку.&lt;br /&gt;
* Найти номер ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Решение :&lt;br /&gt;
&lt;br /&gt;
Построим центроидную декомпозицию &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Изначально посчитаем для каждого центроида, содержащего вершину &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt; посчитаем расстояние до вершины &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Для этого воспользуемся [[Метод_двоичного_подъема|методом двоичных подъемов для поиска lca пары вершин в дереве]], а также тем фактом, что если глубина &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве определена как расстояние от корня до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то длина пути между парой вершин &amp;lt;math&amp;gt;(u, v)&amp;lt;/math&amp;gt; есть &amp;lt;tex&amp;gt;len(u, v) = h(u) + h(v) - 2 \cdot h(lca(u, v))&amp;lt;/tex&amp;gt;. Если изначально предпосчитать проходом [[Обход_в_глубину,_цвета_вершин| dfs]] величины &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, то ответ на запрос &amp;lt;math&amp;gt;len(u, v)&amp;lt;/math&amp;gt; можно делать за время &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с &amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt;доп. памятью. Также можно воспользоваться техникой [[Сведение_задачи_LCA_к_задаче_RMQ|сведения задачи LCA к RMQ]] и решить с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; времени на запрос. Теперь научимся отвечать на запросы. Из последнего свойства центроидной декомпозиции видно, что если &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; - искомая ближайшая помеченная вершина к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то путь между ними содержит центроид &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;, причем &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; - предок одновременно вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве&amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Поэтому заведем [[Красно-черное_дерево|двоичное дерево поиска]] для каждого центроида &amp;lt;math&amp;gt;c \in T&amp;lt;/math&amp;gt;. В этой структуре для каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; будем хранить пары &amp;lt;math&amp;gt;(len(c, u), u)&amp;lt;/math&amp;gt; для всех помеченных вершин &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в поддереве центроидов &amp;lt;math&amp;gt;T(c)&amp;lt;/math&amp;gt;. Когда приходит запрос пометить вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; - добавим в структуру данных для всех предков &amp;lt;math&amp;gt;p_c&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; пары &amp;lt;math&amp;gt;(len(p_c, v), v)&amp;lt;/math&amp;gt;. Мы совершим &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; добавлений, затратив &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; действий на каждое. Запрос снятия пометки с вершины обрабатывается аналогичными удалениями. Запрос поиска ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины - это запрос поиска вершины u, такой что  величина &amp;lt;math&amp;gt;len(c, u) + len(c, v)&amp;lt;/math&amp;gt; минимальна, где &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; - предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов (по пятому свойству, нас интересуют именно такие &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;). Этот запрос занимает так же &amp;lt;tex&amp;gt;O(log^2(n))&amp;lt;/tex&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
Итак, мы научились решать задачу с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и временной сложностью &amp;lt;math&amp;gt;O(log^2(n))&amp;lt;/math&amp;gt; на запрос любого типа с предварительным предпосчетом за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Варианты хранения центроидной декомпозиции ==&lt;br /&gt;
Для хранения дерева центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; есть &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; подхода, имеющих свои преимущества и недостатки :&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева запомним величину &amp;lt;math&amp;gt;p_v&amp;lt;/math&amp;gt; - номер предка вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Этот подход наиболее экономный по памяти (&amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;), но уступает в скорости и функциональности.&lt;br /&gt;
&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева будем хранить весь массив предков &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt; в дереве центроидов.&lt;br /&gt;
&lt;br /&gt;
Этот подход уступает в количестве необходимой дополнительной памяти (&amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt; суммарно), но имеет ряд преимуществ :&lt;br /&gt;
&lt;br /&gt;
1) При проходе по массиву предков фиксированной вершины будет выигрыш в скорости работы, т.к. весь массив будет лежать непрерывным блоком данных и следовательно будет закэширован&lt;br /&gt;
&lt;br /&gt;
2) На массиве предков можно строить различные структуры данных (такие как, например, дерево отрезков) для быстрого (в случае с деревом отрезков &amp;lt;math&amp;gt;O(log(log(n)))&amp;lt;/math&amp;gt; на запрос) поиска предка с необходимыми свойствами. Так, например, в описанной выше задаче про помеченные вершины наибольшего общего предка можно искать методом двоичных подъемов за &amp;lt;math&amp;gt;O(log(log(n))&amp;lt;/math&amp;gt; на запрос, т.к. размер массива предков есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; (по свойству 1 центроидной декомпозиции). Используя эту оптимизацию можно получить время &amp;lt;math&amp;gt;O(log(n)) + O(log(log(n)) = O(log(n))&amp;lt;/math&amp;gt; на запрос нахождения ближайшей помеченной вершины. Чтобы добиться улучшенной асимптотики для запросов изменения можно хранить дерево отрезков на каждом из путей &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt;, в каждой вершине которого хранить двоичное дерево поиска и поддерживать отложенные операции. Тогда ответ на эти запросы будет занимать &amp;lt;math&amp;gt;O(log(n) \cdot log(log(n))&amp;lt;/math&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
&lt;br /&gt;
*[[:Дерево_отрезков._Построение|Дерево отрезков]]&lt;br /&gt;
*[[:Heavy-light_декомпозиция|Heavy-light декомпозиция]]&lt;br /&gt;
*[[:Метод_двоичного_подъема| Метод двоичного подъема]]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы и структуры данных]]&lt;/div&gt;</summary>
		<author><name>92.255.113.52</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61618</id>
		<title>Centroid decomposition</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61618"/>
				<updated>2017-06-14T21:24:12Z</updated>
		
		<summary type="html">&lt;p&gt;92.255.113.52: /* Введение */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Centroid decomposition (рус. центроидная декомпозиция) {{---}} это структура данных, позволяющая отвечать на запросы на дереве. Чаще всего это запросы, связанные с нахождением функции на вершинах, связанных неравенством на расстояние между ними в дереве. Также иногда применяется для запросов на путях в дереве.&lt;br /&gt;
&lt;br /&gt;
== Введение ==&lt;br /&gt;
Рассмотрим &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; задачи на обычном массиве (в дальнейшем мы будем их обобщать на случай дерева):&lt;br /&gt;
&lt;br /&gt;
Задача 1&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть массив &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; положительных целых чисел из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов и числа &amp;lt;tex&amp;gt;W \geqslant 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; индексов массива, таких что &amp;lt;tex&amp;gt;|j - i| \leqslant l &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\sum_{i=0}^{n - 1} a_i \leqslant W&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
Задача 2:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть прямая дорога, на которой расположены &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; городов. В некоторых городах есть госпитали, которые могут принимать больных. Поступают запросы вида :&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;, в котором находится больной и требуется найти такой город &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;|u - v|&amp;lt;/tex&amp;gt; минимально возможное.&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что больше он не будет принимать больных&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что теперь он может принимать больных&lt;br /&gt;
}}&lt;br /&gt;
Для начала решим обе задачи. &lt;br /&gt;
Первая задача решается методом [[Сортировка_слиянием|qevide&amp;amp;conqure (рус. разделяй и властвуй)]] {{---}} давайте разделим массив &amp;lt;tex&amp;gt;a[0...n-1]&amp;lt;/tex&amp;gt; на 2 массива &amp;lt;tex&amp;gt;a[0...\frac{n}{2} - 1]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;a[\frac{n}{2}...n-1]&amp;lt;/tex&amp;gt; и рекурсивно решим задачу для каждого из них. Осталось научиться находить количество искомых пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt;, таких что &amp;lt;tex&amp;gt;i &amp;lt; \frac{n}{2}, j \geqslant \frac{n}{2}&amp;lt;/tex&amp;gt;. Для этого воспользуемся другой известной техникой {{---}} методом двух указателей. Посчитаем массив префиксных сумм для правой половины &amp;lt;tex&amp;gt;pref[i] = \sum_{j=\frac{n}{2}}^{i} a_j&amp;lt;/tex&amp;gt; и суффиксных (&amp;lt;tex&amp;gt;suf[i] = \sum_{j=i}^{\frac{n}{2} + 1} a_j&amp;lt;/tex&amp;gt;) {{---}} для левой. Заведем два указателя (&amp;lt;tex&amp;gt;p_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;p_2&amp;lt;/tex&amp;gt;). Изначально установим &amp;lt;tex&amp;gt;p_1 = \frac{n}{2} - l + 1, p_2 = \frac{n}{2}&amp;lt;/tex&amp;gt;. Пока &amp;lt;tex&amp;gt;p_2 - 1&amp;gt; \frac{n}{2}&amp;lt;/tex&amp;gt; и&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;pref[p_2] + suf[p_1] &amp;gt; W &amp;lt;/tex&amp;gt; будем уменьшать &amp;lt;math&amp;gt;p_2&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Если после этого &amp;lt;math&amp;gt;pref[p_2] + suf[p_1] \leqslant W&amp;lt;/math&amp;gt;, то к ответу прибавим &amp;lt;math&amp;gt;(p_2 - \frac{n}{2} + 1) * (\frac{n}{2} - p_1)&amp;lt;/math&amp;gt;, посго, увеличим &amp;lt;math&amp;gt;p_1&amp;lt;/math&amp;gt; на &amp;lt;math/math&amp;gt;. Так будем делать, пока &amp;lt;math&amp;gt;p_1 &amp;lt; \frac{n}{2}&amp;lt;/math&amp;gt;. В конце сложим текущий ответ и ответы для половин массива {{---}} получим ответ на задачу. Асимптотика такого алгоритма : &amp;lt;tex&amp;gt;T(n) = 2 * T(n / 2) + O(n) = O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вторая задача имеет запросы на изменение и поэтому надо применить динамическую версию qevide&amp;amp;conqure {{---}} [[Дерево_отрезков._Построение|дерево отрезков]]. Построим дерево отрезков,&lt;br /&gt;
поддерживающее 2 вида запросов : присвоение в точке и минимум на отрезке. Изначально сделаем так, чтобы дереву отрезков соответствовал массив &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;b_i = 0&amp;lt;/tex&amp;gt;, если в i-м городе принимает госпиталь и &amp;lt;tex&amp;gt;b_i = 1&amp;lt;/tex&amp;gt; иначе. Когда в каком-то городе открывается/закрывается госпиталь {{---}} делаем запрос на изменение в дереве отрезков. Когда требуется узнать ближайщий госпиталь к &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-му городу, можно воспользоваться одной из следующих идей : &lt;br /&gt;
а) (&amp;lt;tex&amp;gt;O(n \cdot log^2(n)&amp;lt;/tex&amp;gt;) Бинарным поиском ищем ближайший слева и ближайший справа к i-му городу госпиталь (такой город &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, что &amp;lt;tex&amp;gt;\min\limits_{k=i..j}b_k= 1&amp;lt;/tex&amp;gt;). Для этого внутри бинарного поиска каждый раз делаем запрос на поиск минимума в дереве отрезков.  &lt;br /&gt;
б) (&amp;lt;tex&amp;gt;O(n \cdot log(n)&amp;lt;/tex&amp;gt;) Будем одним спуском/подъемом по дереву определять, куда нам нужно идти (в левое или правое поддерево), тем самым делая одновоременно и бинарный поиск, и спуск/подъем по дереву.&lt;br /&gt;
&lt;br /&gt;
== Статическая центроидная декомпозиция ==&lt;br /&gt;
Перейдем к обобщению поставленных задач на случай дерева. Начнем, как и полагается, с первой:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть взвешенное дерево &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин, в каждой вершине которого написаны положительные целые числа. Также по-прежнему даны числа &amp;lt;tex&amp;gt;W &amp;gt;= 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; вершин дерева, таких что расстояние между ними не превосходит &amp;lt;math&amp;gt;l&amp;lt;/math&amp;gt; по числу ребер и не превосходит &amp;lt;math&amp;gt;W&amp;lt;/math&amp;gt; по сумме весов.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Для решения новой задачи применим ту же идею, что и была до этого {{---}} разделяй и властвуй. Для этого нам потребуется следующий объект :&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|id=191213&lt;br /&gt;
|definition =&lt;br /&gt;
'''Центроидом дерева''' (англ. ''centroid'') называется такая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, после удаления которой дерево разбивается на несколько (&amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;) поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,..., t_k&amp;lt;/tex&amp;gt;, таких что для каждого &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; : &amp;lt;tex&amp;gt;|t_i| \leqslant \frac{n}{2}&amp;lt;/tex&amp;gt;, т.е. размер каждого поддерева не превосходит половины размера исходного дерева.&lt;br /&gt;
}}&lt;br /&gt;
Итак, в случае дерева идея разделяй-и-властвуй из предыдущего пункта будет формулироваться так : найдем центроид (доказательство её существования и алгоритм нахождение см. далее). Предположим, что мы сумели найти центроид за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; {{---}} размер дерева. Тогда, как и в упрощенной версии задачи {{---}} рекурсивно найдем ответ для всех поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,..., t_k&amp;lt;/tex&amp;gt;, после чего попытаемся найти недостающие пары вершин, находящиехя в разных поддеревьях и удовлетворяющих вопросу задачи. Для этого будем отвечать на следующие запросы : пусть мы сейчас считаем все пары, где первая из вершин находится в поддереве &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и мы в некоторой структуре данных &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; храним все вершины остальных деревьев (каждую вершину задаем парой &amp;lt;math&amp;gt;(depth(v), length(v))&amp;lt;/math&amp;gt; {{---}} глубина вершины и длина пути до нее из корня поддерева), расстояние до которых от корня их поддерева не превышает &amp;lt;math&amp;gt;min(l, n)&amp;lt;/math&amp;gt;. Тогда просто пройдемся по всем вершинам &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; поддерева &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и прибавим к ответу число вершин в структуре &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt;, таких, что &amp;lt;tex&amp;gt;depth(u) \leqslant l - depth(v)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;length(u) \leqslant l - length(v)&amp;lt;/tex&amp;gt;. Это двумерные запросы, на которые можно отвечать за &amp;lt;math&amp;gt;O(log^2(n)&amp;lt;/math&amp;gt; с помощью [[Многомерное_дерево_отрезков|2d-дерева отрезков]], либо за &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с помощью [[Перечисление_точек_в_произвольном_прямоугольнике_за_n_*_log_%5E(d_-_1)_n_(range_tree)|техники поиска точек в d-мерном пространстве]]. Также читателю предлагается придумать и более эффективные и простые способы решить эту подзадачу.&lt;br /&gt;
&lt;br /&gt;
Оценим итоговую асимптотику : &amp;lt;tex&amp;gt;T(n) = k * T(n / k) + O(n \cdot log(n))&amp;lt;/tex&amp;gt;. Решая это рекурентное соотношение, получим &amp;lt;math&amp;gt;O(n \cdot log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь, как и было обещено, докажем лемму о существовании центроида и опишем алгоритм его эффективного поиска.&lt;br /&gt;
&lt;br /&gt;
=== Лемма о существовании центроида и алгоритм его нахождения. ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
В любом дереве t существует центроид.&lt;br /&gt;
|proof=&lt;br /&gt;
Рассмотрим корень дерева &amp;lt;math&amp;gt;(r)&amp;lt;/math&amp;gt;. Положим изначально &amp;lt;math&amp;gt;v = r&amp;lt;/math&amp;gt;. Изначально &amp;lt;math&amp;gt;|subtree(v)| = n&amp;lt;/math&amp;gt;. Среди всех детей &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; выберем вершину &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; с максимальным размером поддерева. Если &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид, то положим &amp;lt;math&amp;gt;v = u&amp;lt;/math&amp;gt; и продолжим выбор нового u, иначе - остановимся. Докажем, что мы в какой-то момент остановимся. Пусть в призвольный момент времени &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; - не центроид и размер её наддерева меньше &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, значит максимальное поддерево имеет размер больше чем &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, т.е. &amp;lt;math&amp;gt;|subtree(u)| &amp;gt; \frac{n}{2}&amp;lt;/math&amp;gt;, а значит размер &amp;quot;наддерева&amp;quot; вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; равен &amp;lt;tex&amp;gt;n - |subtree(u)| &amp;lt; \frac{n}{2}&amp;lt;/tex&amp;gt;. При этом теперь размер любого поддерева, на которое распадется дерево t при удалении вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; не превосходит &amp;lt;math&amp;gt;|subtree(u)| - 1&amp;lt;/math&amp;gt;, т.к. наддерево имеет размер меньше, чем поддерево &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, а любое поддерево вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; имеет хотя бы на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; вершину меньше (сама вершина &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;). По индукции получаем, что в любой момент времени размер наддерева вершины v меньше &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, значит мы будем спускаться только вниз по дереву &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, и при переходе к вершине &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; - сыну &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; размер максимального поддерева уменьшится как минимум на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Значит не более чем за &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; шагов наши действия прекратятся и мы окажемся в центроиде дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
Итак, мы конструктивно доказали существование центроида и привели линейный относительно размера дерева алгоритм его нахождения. &lt;br /&gt;
}}&lt;br /&gt;
=== Реализация ===&lt;br /&gt;
&lt;br /&gt;
Поиск центроида в дереве:&lt;br /&gt;
&lt;br /&gt;
  '''int''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int''' sz[n])&lt;br /&gt;
     max_subtree = -1&lt;br /&gt;
     '''for''' u : children(v)&lt;br /&gt;
          '''if''' sz[u] &amp;gt; sz[v] / 2 and sz[u] &amp;gt; sz[max_subtree] &amp;lt;font color=green&amp;gt;// считаем sz[-1] = 0 &amp;lt;/font &amp;gt;&lt;br /&gt;
               max_subtree = u&lt;br /&gt;
     '''if''' max_subtree == -1&lt;br /&gt;
          return v&lt;br /&gt;
     '''else''' &lt;br /&gt;
          '''return''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz)&lt;br /&gt;
&lt;br /&gt;
Шаблон решения произвольной задачи на статическую центроидную декомпозицию:&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int''' sz[n])&lt;br /&gt;
     c = &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz) &amp;lt;font color=green&amp;gt;// находим c - центроид поддерева вершины v &amp;lt;/font &amp;gt;&lt;br /&gt;
     ch = children[c]&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{deleteEdges}&amp;lt;/tex&amp;gt;(c, children) &amp;lt;font color=green&amp;gt;// удаляем все ребра между вершиной с и детьми, чтобы мы не смогли из детей попасть в с. Это полезно делать, если решение  подзадачи для поддерева предполагает проход dfs &amp;lt;/font &amp;gt;&lt;br /&gt;
     '''for''' c2 : ch[c]&lt;br /&gt;
          &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;(children, c2, sz)&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{mergeSolution}&amp;lt;/tex&amp;gt;(children, c2) &amp;lt;font color=green&amp;gt;// решаем для текущего поддерева &amp;lt;/font &amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Динамическая центроидная декомпозиция (дерево центроидной декомпозиции) ==&lt;br /&gt;
Теперь вернемся ко второй задаче введения. Для ее решения мы пользовались динамичекой версией devide&amp;amp;conqure {{---}} деревом отрезков. В предыдущем пункте мы определили статический оналог devide&amp;amp;conqure для дерева. Теперь обобщим этот метод для динамических задач.&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Деревом центроидов (или центроидной декомпозицией)''' дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; называют дерево &amp;lt;math&amp;gt;T(t)&amp;lt;/math&amp;gt;, построенное на вершинах дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; следующим образом :&lt;br /&gt;
* Пусть вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; - центроид дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Объявим его корнем нового дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пусть при удалении вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; из дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; оно распалось на поддеревья &amp;lt;tex&amp;gt;t_1, t_2,..., t_k&amp;lt;/tex&amp;gt;, тогда детьми вершины c объявим &amp;lt;tex&amp;gt;T(t_1), T(t_t),..., T(t_k)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
=== Свойства центроидной декомпозиции ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
'''Свойства центроидной декомпозиции''' :&lt;br /&gt;
* Глубина дерева центроидов не превосходит &amp;lt;tex&amp;gt;log(n)&amp;lt;/tex&amp;gt;. &lt;br /&gt;
* Каждая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; является центроидом одного из поддеревьев дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;&lt;br /&gt;
* Каждая вершина дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; поддеревьям дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (еще говорят, что вершина принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; центроидам дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, или что эти центроиды содержат вершину)&lt;br /&gt;
* Для любых вершин &amp;lt;tex&amp;gt;u, v \in T (u \neq v)&amp;lt;/tex&amp;gt; верно ровно одно из следующих трех утверждений : &lt;br /&gt;
a) &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
b) &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
c) &amp;lt;tex&amp;gt;T(u) \cap T(v) = \emptyset &amp;lt;/tex&amp;gt;&lt;br /&gt;
* Простой путь между любой парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; содержит центроид &amp;lt;tex&amp;gt;c \in T(t)&amp;lt;/tex&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
* Действительно, т.к. размер поддерева &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; не превосходит &amp;lt;tex&amp;gt;\frac{|subtree(c)|}{2}&amp;lt;/tex&amp;gt;, то при спуске в каждую следующую вершину на пути к любому листу в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; размер поддерева вершины, в которой мы сейчас находимся, уменьшается как минимум на &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;. Значит длина всего пути до листа не превосходит &amp;lt;math&amp;gt;log(n)&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
* Второе свойство очевидно из построения дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, т.к. если вершина принадлежит дереву центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то она является центроидом, а из построения &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы знаем, что каждая вершина исходного дерева принадлежит и дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Третье свойство - прямое следствие первых двух, т.к. вершина принадлежит любому центроиду &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; т.и т.т., когда c - отец вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов. Т.к. вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; точно принадлежит дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (свойство 2), то она лежит на каком-то пути в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, причем все ее родители (центроиды) ее содержат. А по свойству 1 длина любого вертикального (и даже простого) пути есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
* Четвертое свойство очевидно из того, что &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; - дерево. Т.к. &amp;lt;math&amp;gt;T(u)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;T(v)&amp;lt;/math&amp;gt; - поддеревья различных вершин дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то либо они не пересекаются, либо &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; - предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;, либо &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; - предок &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
* Для доказательства последнего свойства выберем в качестве вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;lca(u, v)&amp;lt;/math&amp;gt; в дереве центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Покажем, что так выбранная вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удовлетворяет заявленным свойствам. То, что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt; - очевидно по определению &amp;lt;math&amp;gt;lca&amp;lt;/math&amp;gt;, т.к. каждый предок любой вершины в дереве центроидов содержит эту вершину. Теперь докажем, что &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежит на пути между парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt;. Т.к. &amp;lt;math&amp;gt;c = lca(u, v)&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то из &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; нет ребра в такого сына, который содержит одновременно &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в своем поддереве (в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;), значит после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; разделится на несколько поддеревьев, таких что вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; окажутся в разных компонентах связности. А значит найдется такое ребро &amp;lt;math&amp;gt;(c, x)&amp;lt;/math&amp;gt;, которое принадлежало пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, но после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удалилось. Это доказывает то, что вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежала на пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
С помощью описанных свойств дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы можем решить задачу 2 для дерева :&lt;br /&gt;
&lt;br /&gt;
=== Пример решения задачи с помощью центроидной декомпозиции === &lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин. В каждый момент времени любая вершина дерева может быть либо помечена, либо нет. Изначально помечена только вершина номер 0. Дан список из &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; запросов : &lt;br /&gt;
* Вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; пометили.&lt;br /&gt;
* С вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; сняли пометку.&lt;br /&gt;
* Найти номер ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Решение :&lt;br /&gt;
&lt;br /&gt;
Построим центроидную декомпозицию &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Изначально посчитаем для каждого центроида, содержащего вершину &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt; посчитаем расстояние до вершины &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Для этого воспользуемся [[Метод_двоичного_подъема|методом двоичных подъемов для поиска lca пары вершин в дереве]], а также тем фактом, что если глубина &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве определена как расстояние от корня до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то длина пути между парой вершин &amp;lt;math&amp;gt;(u, v)&amp;lt;/math&amp;gt; есть &amp;lt;tex&amp;gt;len(u, v) = h(u) + h(v) - 2 \cdot h(lca(u, v))&amp;lt;/tex&amp;gt;. Если изначально предпосчитать проходом [[Обход_в_глубину,_цвета_вершин| dfs]] величины &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, то ответ на запрос &amp;lt;math&amp;gt;len(u, v)&amp;lt;/math&amp;gt; можно делать за время &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с &amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt;доп. памятью. Также можно воспользоваться техникой [[Сведение_задачи_LCA_к_задаче_RMQ|сведения задачи LCA к RMQ]] и решить с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; времени на запрос. Теперь научимся отвечать на запросы. Из последнего свойства центроидной декомпозиции видно, что если &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; - искомая ближайшая помеченная вершина к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то путь между ними содержит центроид &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;, причем &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; - предок одновременно вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве&amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Поэтому заведем [[Красно-черное_дерево|двоичное дерево поиска]] для каждого центроида &amp;lt;math&amp;gt;c \in T&amp;lt;/math&amp;gt;. В этой структуре для каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; будем хранить пары &amp;lt;math&amp;gt;(len(c, u), u)&amp;lt;/math&amp;gt; для всех помеченных вершин &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в поддереве центроидов &amp;lt;math&amp;gt;T(c)&amp;lt;/math&amp;gt;. Когда приходит запрос пометить вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; - добавим в структуру данных для всех предков &amp;lt;math&amp;gt;p_c&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; пары &amp;lt;math&amp;gt;(len(p_c, v), v)&amp;lt;/math&amp;gt;. Мы совершим &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; добавлений, затратив &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; действий на каждое. Запрос снятия пометки с вершины обрабатывается аналогичными удалениями. Запрос поиска ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины - это запрос поиска вершины u, такой что  величина &amp;lt;math&amp;gt;len(c, u) + len(c, v)&amp;lt;/math&amp;gt; минимальна, где &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; - предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов (по пятому свойству, нас интересуют именно такие &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;). Этот запрос занимает так же &amp;lt;tex&amp;gt;O(log^2(n))&amp;lt;/tex&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
Итак, мы научились решать задачу с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и временной сложностью &amp;lt;math&amp;gt;O(log^2(n))&amp;lt;/math&amp;gt; на запрос любого типа с предварительным предпосчетом за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Варианты хранения центроидной декомпозиции ==&lt;br /&gt;
Для хранения дерева центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; есть &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; подхода, имеющих свои преимущества и недостатки :&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева запомним величину &amp;lt;math&amp;gt;p_v&amp;lt;/math&amp;gt; - номер предка вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Этот подход наиболее экономный по памяти (&amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;), но уступает в скорости и функциональности.&lt;br /&gt;
&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева будем хранить весь массив предков &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt; в дереве центроидов.&lt;br /&gt;
&lt;br /&gt;
Этот подход уступает в количестве необходимой дополнительной памяти (&amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt; суммарно), но имеет ряд преимуществ :&lt;br /&gt;
&lt;br /&gt;
1) При проходе по массиву предков фиксированной вершины будет выигрыш в скорости работы, т.к. весь массив будет лежать непрерывным блоком данных и следовательно будет закэширован&lt;br /&gt;
&lt;br /&gt;
2) На массиве предков можно строить различные структуры данных (такие как, например, дерево отрезков) для быстрого (в случае с деревом отрезков &amp;lt;math&amp;gt;O(log(log(n)))&amp;lt;/math&amp;gt; на запрос) поиска предка с необходимыми свойствами. Так, например, в описанной выше задаче про помеченные вершины наибольшего общего предка можно искать методом двоичных подъемов за &amp;lt;math&amp;gt;O(log(log(n))&amp;lt;/math&amp;gt; на запрос, т.к. размер массива предков есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; (по свойству 1 центроидной декомпозиции). Используя эту оптимизацию можно получить время &amp;lt;math&amp;gt;O(log(n)) + O(log(log(n)) = O(log(n))&amp;lt;/math&amp;gt; на запрос нахождения ближайшей помеченной вершины. Чтобы добиться улучшенной асимптотики для запросов изменения можно хранить дерево отрезков на каждом из путей &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt;, в каждой вершине которого хранить двоичное дерево поиска и поддерживать отложенные операции. Тогда ответ на эти запросы будет занимать &amp;lt;math&amp;gt;O(log(n) \cdot log(log(n))&amp;lt;/math&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
&lt;br /&gt;
*[[:Дерево_отрезков._Построение|Дерево отрезков]]&lt;br /&gt;
*[[:Heavy-light_декомпозиция|Heavy-light декомпозиция]]&lt;br /&gt;
*[[:Метод_двоичного_подъема| Метод двоичного подъема]]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы и структуры данных]]&lt;/div&gt;</summary>
		<author><name>92.255.113.52</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61617</id>
		<title>Centroid decomposition</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=Centroid_decomposition&amp;diff=61617"/>
				<updated>2017-06-14T21:23:57Z</updated>
		
		<summary type="html">&lt;p&gt;92.255.113.52: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Centroid decomposition (рус. центроидная декомпозиция) {{---}} это структура данных, позволяющая отвечать на запросы на дереве. Чаще всего это запросы, связанные с нахождением функции на вершинах, связанных неравенством на расстояние между ними в дереве. Также иногда применяется для запросов на путях в дереве.&lt;br /&gt;
&lt;br /&gt;
== Введение ==&lt;br /&gt;
Рассмотрим 2 задачи на обычном массиве (в дальнейшем мы будем их обобщать на случай дерева):&lt;br /&gt;
&lt;br /&gt;
Задача 1&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть массив &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; положительных целых чисел из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов и числа &amp;lt;tex&amp;gt;W \geqslant 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; индексов массива, таких что &amp;lt;tex&amp;gt;|j - i| \leqslant l &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\sum_{i=0}^{n - 1} a_i \leqslant W&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
Задача 2:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть прямая дорога, на которой расположены &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; городов. В некоторых городах есть госпитали, которые могут принимать больных. Поступают запросы вида :&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;, в котором находится больной и требуется найти такой город &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;|u - v|&amp;lt;/tex&amp;gt; минимально возможное.&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что больше он не будет принимать больных&lt;br /&gt;
* дан город &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и сказано, что теперь он может принимать больных&lt;br /&gt;
}}&lt;br /&gt;
Для начала решим обе задачи. &lt;br /&gt;
Первая задача решается методом [[Сортировка_слиянием|qevide&amp;amp;conqure (рус. разделяй и властвуй)]] {{---}} давайте разделим массив &amp;lt;tex&amp;gt;a[0...n-1]&amp;lt;/tex&amp;gt; на 2 массива &amp;lt;tex&amp;gt;a[0...\frac{n}{2} - 1]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;a[\frac{n}{2}...n-1]&amp;lt;/tex&amp;gt; и рекурсивно решим задачу для каждого из них. Осталось научиться находить количество искомых пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt;, таких что &amp;lt;tex&amp;gt;i &amp;lt; \frac{n}{2}, j \geqslant \frac{n}{2}&amp;lt;/tex&amp;gt;. Для этого воспользуемся другой известной техникой {{---}} методом двух указателей. Посчитаем массив префиксных сумм для правой половины &amp;lt;tex&amp;gt;pref[i] = \sum_{j=\frac{n}{2}}^{i} a_j&amp;lt;/tex&amp;gt; и суффиксных (&amp;lt;tex&amp;gt;suf[i] = \sum_{j=i}^{\frac{n}{2} + 1} a_j&amp;lt;/tex&amp;gt;) {{---}} для левой. Заведем два указателя (&amp;lt;tex&amp;gt;p_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;p_2&amp;lt;/tex&amp;gt;). Изначально установим &amp;lt;tex&amp;gt;p_1 = \frac{n}{2} - l + 1, p_2 = \frac{n}{2}&amp;lt;/tex&amp;gt;. Пока &amp;lt;tex&amp;gt;p_2 - 1&amp;gt; \frac{n}{2}&amp;lt;/tex&amp;gt; и&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;pref[p_2] + suf[p_1] &amp;gt; W &amp;lt;/tex&amp;gt; будем уменьшать &amp;lt;math&amp;gt;p_2&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Если после этого &amp;lt;math&amp;gt;pref[p_2] + suf[p_1] \leqslant W&amp;lt;/math&amp;gt;, то к ответу прибавим &amp;lt;math&amp;gt;(p_2 - \frac{n}{2} + 1) * (\frac{n}{2} - p_1)&amp;lt;/math&amp;gt;, посго, увеличим &amp;lt;math&amp;gt;p_1&amp;lt;/math&amp;gt; на &amp;lt;math/math&amp;gt;. Так будем делать, пока &amp;lt;math&amp;gt;p_1 &amp;lt; \frac{n}{2}&amp;lt;/math&amp;gt;. В конце сложим текущий ответ и ответы для половин массива {{---}} получим ответ на задачу. Асимптотика такого алгоритма : &amp;lt;tex&amp;gt;T(n) = 2 * T(n / 2) + O(n) = O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вторая задача имеет запросы на изменение и поэтому надо применить динамическую версию qevide&amp;amp;conqure {{---}} [[Дерево_отрезков._Построение|дерево отрезков]]. Построим дерево отрезков,&lt;br /&gt;
поддерживающее 2 вида запросов : присвоение в точке и минимум на отрезке. Изначально сделаем так, чтобы дереву отрезков соответствовал массив &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;b_i = 0&amp;lt;/tex&amp;gt;, если в i-м городе принимает госпиталь и &amp;lt;tex&amp;gt;b_i = 1&amp;lt;/tex&amp;gt; иначе. Когда в каком-то городе открывается/закрывается госпиталь {{---}} делаем запрос на изменение в дереве отрезков. Когда требуется узнать ближайщий госпиталь к &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-му городу, можно воспользоваться одной из следующих идей : &lt;br /&gt;
а) (&amp;lt;tex&amp;gt;O(n \cdot log^2(n)&amp;lt;/tex&amp;gt;) Бинарным поиском ищем ближайший слева и ближайший справа к i-му городу госпиталь (такой город &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, что &amp;lt;tex&amp;gt;\min\limits_{k=i..j}b_k= 1&amp;lt;/tex&amp;gt;). Для этого внутри бинарного поиска каждый раз делаем запрос на поиск минимума в дереве отрезков.  &lt;br /&gt;
б) (&amp;lt;tex&amp;gt;O(n \cdot log(n)&amp;lt;/tex&amp;gt;) Будем одним спуском/подъемом по дереву определять, куда нам нужно идти (в левое или правое поддерево), тем самым делая одновоременно и бинарный поиск, и спуск/подъем по дереву.&lt;br /&gt;
&lt;br /&gt;
== Статическая центроидная декомпозиция ==&lt;br /&gt;
Перейдем к обобщению поставленных задач на случай дерева. Начнем, как и полагается, с первой:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть взвешенное дерево &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин, в каждой вершине которого написаны положительные целые числа. Также по-прежнему даны числа &amp;lt;tex&amp;gt;W &amp;gt;= 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt;. Требуется найти количество пар &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt; вершин дерева, таких что расстояние между ними не превосходит &amp;lt;math&amp;gt;l&amp;lt;/math&amp;gt; по числу ребер и не превосходит &amp;lt;math&amp;gt;W&amp;lt;/math&amp;gt; по сумме весов.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Для решения новой задачи применим ту же идею, что и была до этого {{---}} разделяй и властвуй. Для этого нам потребуется следующий объект :&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|id=191213&lt;br /&gt;
|definition =&lt;br /&gt;
'''Центроидом дерева''' (англ. ''centroid'') называется такая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, после удаления которой дерево разбивается на несколько (&amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;) поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,..., t_k&amp;lt;/tex&amp;gt;, таких что для каждого &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; : &amp;lt;tex&amp;gt;|t_i| \leqslant \frac{n}{2}&amp;lt;/tex&amp;gt;, т.е. размер каждого поддерева не превосходит половины размера исходного дерева.&lt;br /&gt;
}}&lt;br /&gt;
Итак, в случае дерева идея разделяй-и-властвуй из предыдущего пункта будет формулироваться так : найдем центроид (доказательство её существования и алгоритм нахождение см. далее). Предположим, что мы сумели найти центроид за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; {{---}} размер дерева. Тогда, как и в упрощенной версии задачи {{---}} рекурсивно найдем ответ для всех поддеревьев &amp;lt;tex&amp;gt;t_1, t_2,..., t_k&amp;lt;/tex&amp;gt;, после чего попытаемся найти недостающие пары вершин, находящиехя в разных поддеревьях и удовлетворяющих вопросу задачи. Для этого будем отвечать на следующие запросы : пусть мы сейчас считаем все пары, где первая из вершин находится в поддереве &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и мы в некоторой структуре данных &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; храним все вершины остальных деревьев (каждую вершину задаем парой &amp;lt;math&amp;gt;(depth(v), length(v))&amp;lt;/math&amp;gt; {{---}} глубина вершины и длина пути до нее из корня поддерева), расстояние до которых от корня их поддерева не превышает &amp;lt;math&amp;gt;min(l, n)&amp;lt;/math&amp;gt;. Тогда просто пройдемся по всем вершинам &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; поддерева &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; и прибавим к ответу число вершин в структуре &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt;, таких, что &amp;lt;tex&amp;gt;depth(u) \leqslant l - depth(v)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;length(u) \leqslant l - length(v)&amp;lt;/tex&amp;gt;. Это двумерные запросы, на которые можно отвечать за &amp;lt;math&amp;gt;O(log^2(n)&amp;lt;/math&amp;gt; с помощью [[Многомерное_дерево_отрезков|2d-дерева отрезков]], либо за &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с помощью [[Перечисление_точек_в_произвольном_прямоугольнике_за_n_*_log_%5E(d_-_1)_n_(range_tree)|техники поиска точек в d-мерном пространстве]]. Также читателю предлагается придумать и более эффективные и простые способы решить эту подзадачу.&lt;br /&gt;
&lt;br /&gt;
Оценим итоговую асимптотику : &amp;lt;tex&amp;gt;T(n) = k * T(n / k) + O(n \cdot log(n))&amp;lt;/tex&amp;gt;. Решая это рекурентное соотношение, получим &amp;lt;math&amp;gt;O(n \cdot log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь, как и было обещено, докажем лемму о существовании центроида и опишем алгоритм его эффективного поиска.&lt;br /&gt;
&lt;br /&gt;
=== Лемма о существовании центроида и алгоритм его нахождения. ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
В любом дереве t существует центроид.&lt;br /&gt;
|proof=&lt;br /&gt;
Рассмотрим корень дерева &amp;lt;math&amp;gt;(r)&amp;lt;/math&amp;gt;. Положим изначально &amp;lt;math&amp;gt;v = r&amp;lt;/math&amp;gt;. Изначально &amp;lt;math&amp;gt;|subtree(v)| = n&amp;lt;/math&amp;gt;. Среди всех детей &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; выберем вершину &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; с максимальным размером поддерева. Если &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} не центроид, то положим &amp;lt;math&amp;gt;v = u&amp;lt;/math&amp;gt; и продолжим выбор нового u, иначе - остановимся. Докажем, что мы в какой-то момент остановимся. Пусть в призвольный момент времени &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; - не центроид и размер её наддерева меньше &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, значит максимальное поддерево имеет размер больше чем &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, т.е. &amp;lt;math&amp;gt;|subtree(u)| &amp;gt; \frac{n}{2}&amp;lt;/math&amp;gt;, а значит размер &amp;quot;наддерева&amp;quot; вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; равен &amp;lt;tex&amp;gt;n - |subtree(u)| &amp;lt; \frac{n}{2}&amp;lt;/tex&amp;gt;. При этом теперь размер любого поддерева, на которое распадется дерево t при удалении вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; не превосходит &amp;lt;math&amp;gt;|subtree(u)| - 1&amp;lt;/math&amp;gt;, т.к. наддерево имеет размер меньше, чем поддерево &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, а любое поддерево вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; имеет хотя бы на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; вершину меньше (сама вершина &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;). По индукции получаем, что в любой момент времени размер наддерева вершины v меньше &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;, значит мы будем спускаться только вниз по дереву &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, и при переходе к вершине &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; - сыну &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; размер максимального поддерева уменьшится как минимум на &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;. Значит не более чем за &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; шагов наши действия прекратятся и мы окажемся в центроиде дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
Итак, мы конструктивно доказали существование центроида и привели линейный относительно размера дерева алгоритм его нахождения. &lt;br /&gt;
}}&lt;br /&gt;
=== Реализация ===&lt;br /&gt;
&lt;br /&gt;
Поиск центроида в дереве:&lt;br /&gt;
&lt;br /&gt;
  '''int''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int''' sz[n])&lt;br /&gt;
     max_subtree = -1&lt;br /&gt;
     '''for''' u : children(v)&lt;br /&gt;
          '''if''' sz[u] &amp;gt; sz[v] / 2 and sz[u] &amp;gt; sz[max_subtree] &amp;lt;font color=green&amp;gt;// считаем sz[-1] = 0 &amp;lt;/font &amp;gt;&lt;br /&gt;
               max_subtree = u&lt;br /&gt;
     '''if''' max_subtree == -1&lt;br /&gt;
          return v&lt;br /&gt;
     '''else''' &lt;br /&gt;
          '''return''' &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz)&lt;br /&gt;
&lt;br /&gt;
Шаблон решения произвольной задачи на статическую центроидную декомпозицию:&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;('''int[]''' children[n], '''int''' v, '''int''' sz[n])&lt;br /&gt;
     c = &amp;lt;tex&amp;gt;\mathtt{findCentroid}&amp;lt;/tex&amp;gt;(children, max_subtree, sz) &amp;lt;font color=green&amp;gt;// находим c - центроид поддерева вершины v &amp;lt;/font &amp;gt;&lt;br /&gt;
     ch = children[c]&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{deleteEdges}&amp;lt;/tex&amp;gt;(c, children) &amp;lt;font color=green&amp;gt;// удаляем все ребра между вершиной с и детьми, чтобы мы не смогли из детей попасть в с. Это полезно делать, если решение  подзадачи для поддерева предполагает проход dfs &amp;lt;/font &amp;gt;&lt;br /&gt;
     '''for''' c2 : ch[c]&lt;br /&gt;
          &amp;lt;tex&amp;gt;\mathtt{solve}&amp;lt;/tex&amp;gt;(children, c2, sz)&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{mergeSolution}&amp;lt;/tex&amp;gt;(children, c2) &amp;lt;font color=green&amp;gt;// решаем для текущего поддерева &amp;lt;/font &amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Динамическая центроидная декомпозиция (дерево центроидной декомпозиции) ==&lt;br /&gt;
Теперь вернемся ко второй задаче введения. Для ее решения мы пользовались динамичекой версией devide&amp;amp;conqure {{---}} деревом отрезков. В предыдущем пункте мы определили статический оналог devide&amp;amp;conqure для дерева. Теперь обобщим этот метод для динамических задач.&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Деревом центроидов (или центроидной декомпозицией)''' дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; называют дерево &amp;lt;math&amp;gt;T(t)&amp;lt;/math&amp;gt;, построенное на вершинах дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; следующим образом :&lt;br /&gt;
* Пусть вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; - центроид дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Объявим его корнем нового дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пусть при удалении вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; из дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; оно распалось на поддеревья &amp;lt;tex&amp;gt;t_1, t_2,..., t_k&amp;lt;/tex&amp;gt;, тогда детьми вершины c объявим &amp;lt;tex&amp;gt;T(t_1), T(t_t),..., T(t_k)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
=== Свойства центроидной декомпозиции ===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
'''Свойства центроидной декомпозиции''' :&lt;br /&gt;
* Глубина дерева центроидов не превосходит &amp;lt;tex&amp;gt;log(n)&amp;lt;/tex&amp;gt;. &lt;br /&gt;
* Каждая вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; является центроидом одного из поддеревьев дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;&lt;br /&gt;
* Каждая вершина дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; поддеревьям дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (еще говорят, что вершина принадлежит &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; центроидам дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, или что эти центроиды содержат вершину)&lt;br /&gt;
* Для любых вершин &amp;lt;tex&amp;gt;u, v \in T (u \neq v)&amp;lt;/tex&amp;gt; верно ровно одно из следующих трех утверждений : &lt;br /&gt;
a) &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
b) &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
c) &amp;lt;tex&amp;gt;T(u) \cap T(v) = \emptyset &amp;lt;/tex&amp;gt;&lt;br /&gt;
* Простой путь между любой парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; содержит центроид &amp;lt;tex&amp;gt;c \in T(t)&amp;lt;/tex&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
* Действительно, т.к. размер поддерева &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; не превосходит &amp;lt;tex&amp;gt;\frac{|subtree(c)|}{2}&amp;lt;/tex&amp;gt;, то при спуске в каждую следующую вершину на пути к любому листу в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; размер поддерева вершины, в которой мы сейчас находимся, уменьшается как минимум на &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;. Значит длина всего пути до листа не превосходит &amp;lt;math&amp;gt;log(n)&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
* Второе свойство очевидно из построения дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, т.к. если вершина принадлежит дереву центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то она является центроидом, а из построения &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы знаем, что каждая вершина исходного дерева принадлежит и дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Третье свойство - прямое следствие первых двух, т.к. вершина принадлежит любому центроиду &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; т.и т.т., когда c - отец вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов. Т.к. вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; точно принадлежит дереву &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; (свойство 2), то она лежит на каком-то пути в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, причем все ее родители (центроиды) ее содержат. А по свойству 1 длина любого вертикального (и даже простого) пути есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
* Четвертое свойство очевидно из того, что &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; - дерево. Т.к. &amp;lt;math&amp;gt;T(u)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;T(v)&amp;lt;/math&amp;gt; - поддеревья различных вершин дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то либо они не пересекаются, либо &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; - предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(v) \subset T(u)&amp;lt;/tex&amp;gt;, либо &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; - предок &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, и значит &amp;lt;tex&amp;gt;T(u) \subset T(v)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
* Для доказательства последнего свойства выберем в качестве вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;lca(u, v)&amp;lt;/math&amp;gt; в дереве центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Покажем, что так выбранная вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удовлетворяет заявленным свойствам. То, что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt; - очевидно по определению &amp;lt;math&amp;gt;lca&amp;lt;/math&amp;gt;, т.к. каждый предок любой вершины в дереве центроидов содержит эту вершину. Теперь докажем, что &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежит на пути между парой вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt;. Т.к. &amp;lt;math&amp;gt;c = lca(u, v)&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то из &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; нет ребра в такого сына, который содержит одновременно &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в своем поддереве (в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;), значит после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; разделится на несколько поддеревьев, таких что вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; окажутся в разных компонентах связности. А значит найдется такое ребро &amp;lt;math&amp;gt;(c, x)&amp;lt;/math&amp;gt;, которое принадлежало пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, но после удаления &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; удалилось. Это доказывает то, что вершина &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; лежала на пути из &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, ч.т.д.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
С помощью описанных свойств дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; мы можем решить задачу 2 для дерева :&lt;br /&gt;
&lt;br /&gt;
=== Пример решения задачи с помощью центроидной декомпозиции === &lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть дерево &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин. В каждый момент времени любая вершина дерева может быть либо помечена, либо нет. Изначально помечена только вершина номер 0. Дан список из &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; запросов : &lt;br /&gt;
* Вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; пометили.&lt;br /&gt;
* С вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; сняли пометку.&lt;br /&gt;
* Найти номер ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Решение :&lt;br /&gt;
&lt;br /&gt;
Построим центроидную декомпозицию &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; дерева &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;. Изначально посчитаем для каждого центроида, содержащего вершину &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt; посчитаем расстояние до вершины &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Для этого воспользуемся [[Метод_двоичного_подъема|методом двоичных подъемов для поиска lca пары вершин в дереве]], а также тем фактом, что если глубина &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве определена как расстояние от корня до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то длина пути между парой вершин &amp;lt;math&amp;gt;(u, v)&amp;lt;/math&amp;gt; есть &amp;lt;tex&amp;gt;len(u, v) = h(u) + h(v) - 2 \cdot h(lca(u, v))&amp;lt;/tex&amp;gt;. Если изначально предпосчитать проходом [[Обход_в_глубину,_цвета_вершин| dfs]] величины &amp;lt;math&amp;gt;h(v)&amp;lt;/math&amp;gt; за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, то ответ на запрос &amp;lt;math&amp;gt;len(u, v)&amp;lt;/math&amp;gt; можно делать за время &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; с &amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt;доп. памятью. Также можно воспользоваться техникой [[Сведение_задачи_LCA_к_задаче_RMQ|сведения задачи LCA к RMQ]] и решить с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; времени на запрос. Теперь научимся отвечать на запросы. Из последнего свойства центроидной декомпозиции видно, что если &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; - искомая ближайшая помеченная вершина к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, то путь между ними содержит центроид &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;, такой что &amp;lt;tex&amp;gt;u, v \in T(c)&amp;lt;/tex&amp;gt;, причем &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; - предок одновременно вершин &amp;lt;math&amp;gt;u, v&amp;lt;/math&amp;gt; в дереве&amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Поэтому заведем [[Красно-черное_дерево|двоичное дерево поиска]] для каждого центроида &amp;lt;math&amp;gt;c \in T&amp;lt;/math&amp;gt;. В этой структуре для каждой вершины &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; будем хранить пары &amp;lt;math&amp;gt;(len(c, u), u)&amp;lt;/math&amp;gt; для всех помеченных вершин &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; в поддереве центроидов &amp;lt;math&amp;gt;T(c)&amp;lt;/math&amp;gt;. Когда приходит запрос пометить вершину &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; - добавим в структуру данных для всех предков &amp;lt;math&amp;gt;p_c&amp;lt;/math&amp;gt; вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; пары &amp;lt;math&amp;gt;(len(p_c, v), v)&amp;lt;/math&amp;gt;. Мы совершим &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; добавлений, затратив &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; действий на каждое. Запрос снятия пометки с вершины обрабатывается аналогичными удалениями. Запрос поиска ближайшей к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; помеченной вершины - это запрос поиска вершины u, такой что  величина &amp;lt;math&amp;gt;len(c, u) + len(c, v)&amp;lt;/math&amp;gt; минимальна, где &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; - предок &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве центроидов (по пятому свойству, нас интересуют именно такие &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;). Этот запрос занимает так же &amp;lt;tex&amp;gt;O(log^2(n))&amp;lt;/tex&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
Итак, мы научились решать задачу с &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; дополнительной памятью и временной сложностью &amp;lt;math&amp;gt;O(log^2(n))&amp;lt;/math&amp;gt; на запрос любого типа с предварительным предпосчетом за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Варианты хранения центроидной декомпозиции ==&lt;br /&gt;
Для хранения дерева центроидов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; есть &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; подхода, имеющих свои преимущества и недостатки :&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева запомним величину &amp;lt;math&amp;gt;p_v&amp;lt;/math&amp;gt; - номер предка вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в дереве &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Этот подход наиболее экономный по памяти (&amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;), но уступает в скорости и функциональности.&lt;br /&gt;
&lt;br /&gt;
* Для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного дерева будем хранить весь массив предков &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt; в дереве центроидов.&lt;br /&gt;
&lt;br /&gt;
Этот подход уступает в количестве необходимой дополнительной памяти (&amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt; суммарно), но имеет ряд преимуществ :&lt;br /&gt;
&lt;br /&gt;
1) При проходе по массиву предков фиксированной вершины будет выигрыш в скорости работы, т.к. весь массив будет лежать непрерывным блоком данных и следовательно будет закэширован&lt;br /&gt;
&lt;br /&gt;
2) На массиве предков можно строить различные структуры данных (такие как, например, дерево отрезков) для быстрого (в случае с деревом отрезков &amp;lt;math&amp;gt;O(log(log(n)))&amp;lt;/math&amp;gt; на запрос) поиска предка с необходимыми свойствами. Так, например, в описанной выше задаче про помеченные вершины наибольшего общего предка можно искать методом двоичных подъемов за &amp;lt;math&amp;gt;O(log(log(n))&amp;lt;/math&amp;gt; на запрос, т.к. размер массива предков есть &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; (по свойству 1 центроидной декомпозиции). Используя эту оптимизацию можно получить время &amp;lt;math&amp;gt;O(log(n)) + O(log(log(n)) = O(log(n))&amp;lt;/math&amp;gt; на запрос нахождения ближайшей помеченной вершины. Чтобы добиться улучшенной асимптотики для запросов изменения можно хранить дерево отрезков на каждом из путей &amp;lt;math&amp;gt;p[v]&amp;lt;/math&amp;gt;, в каждой вершине которого хранить двоичное дерево поиска и поддерживать отложенные операции. Тогда ответ на эти запросы будет занимать &amp;lt;math&amp;gt;O(log(n) \cdot log(log(n))&amp;lt;/math&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
&lt;br /&gt;
*[[:Дерево_отрезков._Построение|Дерево отрезков]]&lt;br /&gt;
*[[:Heavy-light_декомпозиция|Heavy-light декомпозиция]]&lt;br /&gt;
*[[:Метод_двоичного_подъема| Метод двоичного подъема]]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы и структуры данных]]&lt;/div&gt;</summary>
		<author><name>92.255.113.52</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D1%80%D0%B8%D1%84%D0%BC%D0%B5%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BA%D0%BE%D0%B4%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5&amp;diff=60036</id>
		<title>Арифметическое кодирование</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D1%80%D0%B8%D1%84%D0%BC%D0%B5%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BA%D0%BE%D0%B4%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5&amp;diff=60036"/>
				<updated>2017-01-18T18:16:18Z</updated>
		
		<summary type="html">&lt;p&gt;92.255.113.52: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Convex hull trick {{---}} один из методов оптимизации динамического программирования [[http://neerc.ifmo.ru/wiki/index.php?title=Динамическое_программирование]], использующий идею выпуклой оболочки. Позволяет улучшить ассимптотику решения некоторых задачь, решемых методом динамического программирования с &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt; до &amp;lt;tex&amp;gt;O(n\cdot\log(n))&amp;lt;/tex&amp;gt;. Техника впервые появилась в 1995 году (задачу на нее предложили в USACO {{---}} национальной олимпиаде США по программированию). Массовую известность получила после IOI (международной олимпиады по программированию для школьников) 2002.&lt;br /&gt;
                                                                                  &lt;br /&gt;
                                                                                  ==Пример задачи, решаемой методом convex hull trick==&lt;br /&gt;
                                                                                  Рассмотрим задачу на ДП:&lt;br /&gt;
                                                                                  {{Задача&lt;br /&gt;
                                                                                  |definition = Есть &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; деревьев с высотами &amp;lt;tex&amp;gt;a_1, a_2, \dots, a_n&amp;lt;/tex&amp;gt; (в метрах). Требуется спилить их все, потратив минимальное количество монет на заправку&lt;br /&gt;
                                                                                  бензопилы. Но пила устроена так, что она может спиливать только по 1 метру от дерева, к которому ее применили. Также после&lt;br /&gt;
                                                                                  срубленного метра (любого дерева) пилу нужно заправлять, платя за  бензин определенной кол-во монет. Причем стоимость&lt;br /&gt;
                                                                                  бензина зависит от срубленных (полностью) деревьев. Если сейчас максимальный индекс срубленного дерева равен &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, то цена заправки&lt;br /&gt;
                                                                                  равна &amp;lt;tex&amp;gt;c_i&amp;lt;/tex&amp;gt;.  Изначально пила заправлена.&lt;br /&gt;
                                                                                  Также известны следующие ограничения : &amp;lt;tex&amp;gt;c_n = 0, a_1 = 1, a_i&amp;lt;/tex&amp;gt; возрастают, &amp;lt;tex&amp;gt;c_i&amp;lt;/tex&amp;gt; убывают. Изначально пила заправлена.&lt;br /&gt;
                                                                                  (убывание и возрастание нестрогие)&lt;br /&gt;
                                                                                  }}&lt;br /&gt;
                                                                                  (Задача H с Санкт-Петербургских сборов к РОИ 2016[http://neerc.ifmo.ru/school/camp-2016/problems/20160318a.pdf])&lt;br /&gt;
                                                                                                                                    &amp;lt;/noinclude&amp;gt;&lt;br /&gt;
                                                                                                                                    &amp;lt;includeonly&amp;gt;{{#if: {{{neat|}}}|&lt;br /&gt;
                                                                                                                                    &amp;lt;div style=&amp;quot;background-color: #fcfcfc; float:left;&amp;quot;&amp;gt;&lt;br /&gt;
                                                                                                                                    &amp;lt;div style=&amp;quot;background-color: #ddd;&amp;quot;&amp;gt;'''Задача:'''&amp;lt;/div&amp;gt;&lt;br /&gt;
                                                                                                                                    &amp;lt;div style=&amp;quot;border:1px dashed #2f6fab; padding: 8px; font-style: italic;&amp;quot;&amp;gt;{{{definition}}}&amp;lt;/div&amp;gt;&lt;br /&gt;
                                                                                                                                    &amp;lt;/div&amp;gt;|&lt;br /&gt;
                                                                                                                                    &amp;lt;table border=&amp;quot;0&amp;quot; width=&amp;quot;100%&amp;quot;&amp;gt;&lt;br /&gt;
                                                                                                                                    &amp;lt;tr&amp;gt;&amp;lt;td style=&amp;quot;background-color: #ddd&amp;quot;&amp;gt;'''Задача:'''&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
                                                                                                                                    &amp;lt;tr&amp;gt;&amp;lt;td style=&amp;quot;border:1px dashed #2f6fab; padding: 8px; background-color: #fcfcfc; font-style: italic;&amp;quot;&amp;gt;{{{definition}}}&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
                                                                                                                                    &amp;lt;/table&amp;gt;}}&lt;br /&gt;
                                                                                                                                    &amp;lt;/includeonly&amp;gt;&lt;br /&gt;
                                                                                                                                    &lt;br /&gt;
                                                                                                                                    ==Наивное решение==&lt;br /&gt;
                                                                                                                                    Сначала заметим важный факт : т.к. &amp;lt;tex&amp;gt;c[i]&amp;lt;/tex&amp;gt; убывают (нестрого) и &amp;lt;tex&amp;gt;c[n] = 0&amp;lt;/tex&amp;gt;, то все &amp;lt;tex&amp;gt;c[i]&amp;lt;/tex&amp;gt; неотрицательны.&lt;br /&gt;
                                                                                                                                    Понятно, что нужно затратив минимальную стоимость срубить последнее (&amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;-е) дерево, т.к. после него все деревья можно будет рубить бесплатно (т.к. &amp;lt;tex&amp;gt;c[n] = 0&amp;lt;/tex&amp;gt;). Посчитаем следующую динамику : &amp;lt;tex&amp;gt;dp[i]&amp;lt;/tex&amp;gt; {{---}} минимальная стоимость, заплатив которую можно добиться того, что дерево номер &amp;lt;tex&amp;gt;i.&amp;lt;/tex&amp;gt; будет срублено.&lt;br /&gt;
                                                                                                                                    База динамики : &amp;lt;tex&amp;gt;dp[1] = 0&amp;lt;/tex&amp;gt;, т.к. изначально пила заправлена и высота первого дерева равна 1, по условию задачи.&lt;br /&gt;
                                                                                                                                    Переход динамики :  понятно, что выгодно рубить сначала более дорогие и низкие деревья, а потом более высокие и дешевые (док-во этого факта оставляется читателям как несложное упражнение, т.к. эта идея относится скорее к теме жадных алгоритмнов, чем к теме данной статьи). Поэтому перед  &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-м деревом мы обязательно срубили какое-то &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-е, причем &amp;lt;tex&amp;gt;j \leqslant i - 1&amp;lt;/tex&amp;gt;. Поэтому чтобы найти &amp;lt;tex&amp;gt;dp[i]&amp;lt;/tex&amp;gt; нужно перебрать все &amp;lt;tex&amp;gt;1 \leqslant j \leqslant i - 1&amp;lt;/tex&amp;gt; и попытаться использовать ответ для дерева намер &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;. Итак, пусть перед &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-м деревом мы полностью срубили &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-е, причем высота &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева составляет &amp;lt;tex&amp;gt;a[i]&amp;lt;/tex&amp;gt;, а т.к. последнее дерево, которое мы срубили имеет индекс &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, то стоимость каждого метра &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева составит &amp;lt;tex&amp;gt;c[j]&amp;lt;/tex&amp;gt;.  Поэтому на сруб &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева мы потратим &amp;lt;tex&amp;gt;a[i] \cdot c[j]&amp;lt;/tex&amp;gt; монет. Также не стоит забывать, ситуацию, когда  &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-е дерево полностью срублено, мы получили не бесплатно, а за &amp;lt;tex&amp;gt;dp[j]&amp;lt;/tex&amp;gt; монет.&lt;br /&gt;
                                                                                                                                    Итогвая формула пересчета : &amp;lt;tex&amp;gt;dp[i] = \min\limits_{j=1...i-1} (dp[j] + a[i] \cdot c[j])&amp;lt;/tex&amp;gt;.&lt;br /&gt;
                                                                                                                                    &lt;br /&gt;
                                                                                                                                    Посмотрим на код выше описанного решения:&lt;br /&gt;
                                                                                                                                    '''int''' &amp;lt;tex&amp;gt;\mathtt{simpleDP}&amp;lt;/tex&amp;gt;('''int''' a[n], '''int''' c[n])&lt;br /&gt;
                                                                                                                                    dp[1] = 0&lt;br /&gt;
                                                                                                                                    dp[2] = dp[3] = ... = dp[n] = &amp;lt;tex&amp;gt;\infty&amp;lt;/tex&amp;gt;&lt;br /&gt;
                                                                                                                                    '''for'''  i = 1..n-1&lt;br /&gt;
                                                                                                                                    dp[i] = &amp;lt;tex&amp;gt;+\infty&amp;lt;/tex&amp;gt;&lt;br /&gt;
                                                                                                                                    '''for''' j = 0..i-1&lt;br /&gt;
                                                                                                                                    '''if''' (dp[j] + a[i] &amp;lt;tex&amp;gt;\cdot&amp;lt;/tex&amp;gt; c[j] &amp;lt; dp[i])&lt;br /&gt;
                                                                                                                                    dp[i] = dp[j] + a[i] &amp;lt;tex&amp;gt;\cdot&amp;lt;/tex&amp;gt; c[j]&lt;br /&gt;
                                                                                                                                    '''return''' dp[n]&lt;br /&gt;
                                                                                                                                    Нетрудно видеть, что такая динамика работает за &amp;lt;tex&amp;gt;O(n^2)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
                                                                                                                                    &lt;br /&gt;
                                                                                                                                    ==Ключевая идея оптимизации==&lt;br /&gt;
                                                                                                                                    Для начала сделаем замену обозначений. Давайте обозначим &amp;lt;tex&amp;gt;dp[j]&amp;lt;/tex&amp;gt; за &amp;lt;tex&amp;gt;b[j]&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;a[i]&amp;lt;/tex&amp;gt; за &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt;, а &amp;lt;tex&amp;gt;c[j]&amp;lt;/tex&amp;gt; за &amp;lt;tex&amp;gt;k[j]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
                                                                                                                                    &lt;br /&gt;
                                                                                                                                    Теперь формула приняла вид &amp;lt;tex&amp;gt;dp[i] = \min\limits_{j=0...i-1}(k[j] \cdot x[i] + b[j])&amp;lt;/tex&amp;gt;. Выражение &amp;lt;tex&amp;gt;k[j] \cdot x + b[j]&amp;lt;/tex&amp;gt; {{---}} это в точности уравнение прямой вида &amp;lt;tex&amp;gt;y = kx + b&amp;lt;/tex&amp;gt;.&lt;br /&gt;
                                                                                                                                    &lt;br /&gt;
                                                                                                                                    Сопоставим каждому &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, обработанному ранее, прямую &amp;lt;tex&amp;gt;y[j](x) = k[j] \cdot x + b[j]&amp;lt;/tex&amp;gt;. Из условия «&amp;lt;tex&amp;gt;c[i]&amp;lt;/tex&amp;gt; убывают &amp;lt;tex&amp;gt;\Leftrightarrow  k[j]&amp;lt;/tex&amp;gt; уменьшаются с номером &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;» следует то, что прямые, полученные ранее отсортированы в порядке убывания углового коэффициент. Давайте нарисуем несколько таких прямых :&lt;br /&gt;
                                                                                                                                    &lt;br /&gt;
                                                                                                                                    [[Файл:picture1convexhull.png]]&lt;br /&gt;
                                                                                                                                    &lt;br /&gt;
                                                                                                                                    Выделим множество точек &amp;lt;tex&amp;gt;(x0, y0)&amp;lt;/tex&amp;gt; , таких что все они принадлежат одной из прямых и при этом нету ни одной прямой &amp;lt;tex&amp;gt;y’(x)&amp;lt;/tex&amp;gt;, такой что &amp;lt;tex&amp;gt;y’(x0) &amp;lt; y0&amp;lt;/tex&amp;gt;. Иными словами возьмем «выпуклую (вверх) оболочку» нашего множества прямых (её еще называют нижней ошибающей множества прямых на плоскости). Назовем ее «&amp;lt;tex&amp;gt;y = convex(x)&amp;lt;/tex&amp;gt;». Видно, что множество точек &amp;lt;math&amp;gt;(x, convex(x))&amp;lt;/math&amp;gt; представляет собой выпуклую вверх функцию.&lt;br /&gt;
                                                                                                                                    &lt;br /&gt;
                                                                                                                                    ==Цель нижней огибающей множества прямых==&lt;br /&gt;
                                                                                                                                    Пусть мы считаем динамику для &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева. Его задает &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt;. Итак, нам нужно для данного &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt; найти &amp;lt;tex&amp;gt;\min\limits_{j=0..i-1}(k[j] \cdot x[i] + b[i]) = \min\limits_{j=0..i-1}(y[j](x[i]))&amp;lt;/tex&amp;gt;. Это выражение есть &amp;lt;math&amp;gt;convex(x[i])&amp;lt;/math&amp;gt;. Из монотонности угловых коэффицентов отрезков, задающих выпуклую оболочку, и их расположения по координаты x следует то, что отрезок, который пересекает прямую &amp;lt;tex&amp;gt;x = x[i]&amp;lt;/tex&amp;gt;, можно найти бинарным поиском. Это потребует &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; времени на поиск такого &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;dp[i] = k[j] \cdot x[i] + b[j]&amp;lt;/tex&amp;gt;. Теперь осталось научиться поддерживать множество прямых и быстро добавлять &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-ю прямую после того, как мы посчитали &amp;lt;tex&amp;gt;b[i] = dp[i]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
                                                                                                                                    &lt;br /&gt;
                                                                                                                                    Воспользуемся идеей алгоритма построения выпуклой оболочки множества точек. Заведем 2 стека &amp;lt;tex&amp;gt;k[]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;b[]&amp;lt;/tex&amp;gt;, которые задают прямые в отсортированном порядке их угловыми коэффицентами и свободными членами. Рассмотрим ситуацию, когда мы хотим добавить новую (&amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-тую) прямую в множество. Пусть сейчас в множестве лежит &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt; прямых (нумерация с 1). Пусть &amp;lt;tex&amp;gt;(xL, yL)&amp;lt;/tex&amp;gt; {{---}} точка пересечения &amp;lt;tex&amp;gt;sz - 1&amp;lt;/tex&amp;gt;-й прямой множества и &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-й, а &amp;lt;tex&amp;gt;(xR, yR)&amp;lt;/tex&amp;gt; {{---}} точка пересечения новой прямой, которую мы хотим добавить в конец множества и &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-й. Нас будут интересовать только их &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;-овые координаты &amp;lt;tex&amp;gt;xL&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;xR&amp;lt;/tex&amp;gt;, соответственно. Если оказалось, что новая прямая пересекает &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-ю прямую выпуклой оболочки позже, чем &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-я &amp;lt;tex&amp;gt;sz - 1&amp;lt;/tex&amp;gt;-ю, т.е. &amp;lt;tex&amp;gt;(xL \geqslant xR)&amp;lt;/tex&amp;gt;, то &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-ю удалим из нашего множества, иначе {{---}} остановимся. Так будем делать, пока либо кол-во прямых в стеке не станет равным 2, либо &amp;lt;tex&amp;gt;xL&amp;lt;/tex&amp;gt; не станет меньше &amp;lt;tex&amp;gt;xR.&amp;lt;/tex&amp;gt;&lt;br /&gt;
                                                                                                                                    &lt;br /&gt;
                                                                                                                                    Асимптотика : аналогично обычному алгоритму построения выпуклой оболочки, каждая прямая ровно &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; раз добавится в стек и максимум &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; раз удалится. Значит время работы перестройки выпуклой оболочки займет &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; суммарно.&lt;br /&gt;
                                                                                                                                    &lt;br /&gt;
                                                                                                                                    [[Файл:picture2convexhull.png]]&lt;br /&gt;
                                                                                                                                    [[Файл:picture3convexhull.png]]&lt;br /&gt;
                                                                                                                                    &lt;br /&gt;
                                                                                                                                    {{Теорема&lt;br /&gt;
                                                                                                                                    |id=th1239.&lt;br /&gt;
                                                                                                                                    |statement=Алгоритм построения нижней огибающей множества прямых корректен.&lt;br /&gt;
                                                                                                                                    |proof=Достаточно показать, что последнюю прямую нужно удалить из множества т.и т.т., когда она наша новая прямая пересекает ее в точке с координатой по оси X, меньшей, чем последняя {{---}} предпоследнюю.&lt;br /&gt;
                                                                                                                                    &lt;br /&gt;
                                                                                                                                    Пусть &amp;lt;tex&amp;gt;Y(x) = Kx + B&amp;lt;/tex&amp;gt; {{---}} уравнение новой прямой,  &amp;lt;tex&amp;gt;y[i](x) = K[i]x + B[i]&amp;lt;/tex&amp;gt; {{---}} уравнения прямых множества. Тогда т.к. &amp;lt;tex&amp;gt;K &amp;lt; K[sz]&amp;lt;/tex&amp;gt;, то при &amp;lt;tex&amp;gt;x \in [- \infty; xR]  : y[sz](x) &amp;lt;= Y(x)&amp;lt;/tex&amp;gt;, а т.к. &amp;lt;tex&amp;gt; K[sz] &amp;lt; K[sz - 1]&amp;lt;/tex&amp;gt;, то при &amp;lt;tex&amp;gt;x \in [xL; + \infty]  : y[sz - 1](x) \geqslant y[sz](x)&amp;lt;/tex&amp;gt;. Если &amp;lt;tex&amp;gt;xL &amp;lt; xR&amp;lt;/tex&amp;gt;, то при &amp;lt;tex&amp;gt;x \in [xL; xR]  : y[sz - 1] \geqslant y[sz](x) и Y(x) \geqslant y[sz](x)&amp;lt;/tex&amp;gt;, т.е. на отрезке &amp;lt;tex&amp;gt;[xL; xR]&amp;lt;/tex&amp;gt; прямая номер sz лежит ниже остальных и её нужно оставить в множестве. Если же &amp;lt;tex&amp;gt;xL &amp;gt; xR&amp;lt;/tex&amp;gt;, то она ниже всех на отрезке &amp;lt;tex&amp;gt;[xL; xR] = \varnothing &amp;lt;/tex&amp;gt;, т.е. её можно удалить из множества&lt;br /&gt;
                                                                                                                                    }}&lt;br /&gt;
                                                                                                                                    &lt;br /&gt;
                                                                                                                                    ==Детали реализации:==&lt;br /&gt;
                                                                                                                                    Будем хранить 2 массива : &amp;lt;tex&amp;gt;front[]&amp;lt;/tex&amp;gt; {{---}} &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;-координаты, начиная с которых прямые совпадают с выпуклой оболочкой (т.е. i-я прямая совпадает с выпуклой оболочкой текущего множества прямых при &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;[front[i]; front[i + 1])&amp;lt;/tex&amp;gt; ) и &amp;lt;tex&amp;gt;st[]&amp;lt;/tex&amp;gt; {{---}} номера деревьев, соответствующих прямым (т.е. &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-я прямая множества, где &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;[1; sz]&amp;lt;/tex&amp;gt; соответствует дереву номер &amp;lt;tex&amp;gt;sz[i]&amp;lt;/tex&amp;gt;). Также воспользуемся тем, что &amp;lt;tex&amp;gt;x[i] = a[i]&amp;lt;/tex&amp;gt; возрастают (по условию задачи), а значит мы можем искать первое такое &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;x[i] \geqslant front[j]&amp;lt;/tex&amp;gt; не бинарным поиском, а методом двух указателей за &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; операций суммарно. Также массив front[] можно хранить в целых числах, округляя х-координаты в сторону лежащих правее по оси x до ближайшего целого (*), т.к. на самом деле мы, считая динамику, подставляем в уравнения прямых только целые &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt;, а значит если &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt;-я прямая пересекается с &amp;lt;tex&amp;gt;k+1&amp;lt;/tex&amp;gt;-й в точке &amp;lt;tex&amp;gt;z +&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\alpha&amp;lt;/tex&amp;gt; (&amp;lt;math&amp;gt;z&amp;lt;/math&amp;gt;-целое,  &amp;lt;tex&amp;gt;\alpha&amp;lt;/tex&amp;gt;  &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;[0;1)&amp;lt;/tex&amp;gt;), то мы будем подставлять в их уравнения &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt;  или &amp;lt;tex&amp;gt;z + 1&amp;lt;/tex&amp;gt;. Поэтому можно считать, что новая прямая начинает совпадать с выпуклой оболочкой, начиная с &amp;lt;tex&amp;gt;x = z+1&amp;lt;/tex&amp;gt;&lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     &lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     ==Реализация==&lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     '''int''' &amp;lt;tex&amp;gt;\mathtt{ConvexHullTrick}&amp;lt;/tex&amp;gt;('''int''' a[n], '''int''' c[n])&lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     st[1] = 1&lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     from[1] = -&amp;lt;tex&amp;gt;\infty&amp;lt;/tex&amp;gt;&amp;lt;font color=green&amp;gt;// первая прямая покрывает все x-ы, начиная с -∞ &amp;lt;/font&amp;gt;&lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     sz = 1 &amp;lt;font color=green&amp;gt;// текущий размер выпуклой оболочки &amp;lt;/font&amp;gt;&lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     pos = 1 &amp;lt;font color=green&amp;gt;// текущая позиция первого такого j, что x[i] \geqslant front[st[j]] &amp;lt;/font &amp;gt;&lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     '''for'''  i = 2..n  &lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     '''while''' (front[pos] &amp;lt; x[i]) &amp;lt;font color=green&amp;gt;// метод 1 указателя (ищем первое pos, такое что x[i] покрывается &amp;quot;областью действия&amp;quot; st[pos]-той прямой &amp;lt;/font &amp;gt;&lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     pos = pos + 1&lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     j = st[pos]&lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     dp[i] = K[j]&amp;lt;math&amp;gt;\cdot&amp;lt;/math&amp;gt;a[i] + B[j] &lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     '''if''' (i &amp;lt; n)   &amp;lt;font color=green&amp;gt;// если у нас добавляется НЕ последняя прямая, то придется пересчитать выпуклую оболочку &amp;lt;/font &amp;gt;&lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     K[i] = c[i]  &amp;lt;font color=green&amp;gt;// наши переобозначения переменных &amp;lt;/font &amp;gt;&lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     B[i] = dp[i] &amp;lt;font color=green&amp;gt;// наши переобозначения переменных &amp;lt;/font &amp;gt;&lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     x = -&amp;lt;tex&amp;gt;\infty&amp;lt;/tex&amp;gt; &lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     '''while''' ''true'' &lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     j = st[sz]&lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     x = divide(B[j] - B[i], K[i] - K[j]) &amp;lt;font color=green&amp;gt;// x-координата пересечения с последней прямой оболочки, округленное в нужную сторону (*) &amp;lt;/font &amp;gt;&lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     '''if''' (x &amp;gt; from[sz]) '''break'''  &amp;lt;font color=green&amp;gt;// перестаем удалять последнюю прямую из множества, если новая прямая пересекает ее позже, чем начинается ее &amp;quot;область действия&amp;quot; &amp;lt;/font &amp;gt;&lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     sz = sz - 1&amp;lt;font color=green&amp;gt;// удаляем последнюю прямую, если она лишняя &amp;lt;/font &amp;gt;&lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     st[sz + 1] = i  &lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     from[sz + 1] = x &amp;lt;font color=green&amp;gt;// добавили новую прямую &amp;lt;/font &amp;gt;&lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     sz = sz + 1&lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     '''return''' dp[n] &lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     Здесь функция &amp;lt;tex&amp;gt;\mathtt{divide}&amp;lt;/tex&amp;gt;(a, b) возвращает нужное(*) округление a / b. Приведем её код :&lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     '''int''' &amp;lt;tex&amp;gt;\mathtt{divide}&amp;lt;/tex&amp;gt;('''int''' a, '''int''' b)&lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     delta = 0&lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     '''if''' (a '''mod''' b ≠ 0) delta = 1&lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     '''if''' ((a &amp;gt; 0 '''and''' b &amp;gt; 0) '''or''' (a &amp;lt; 0 '''and''' b &amp;lt; 0)) '''return''' [a / b] + delta&lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     '''return''' -[|a| / |b|]&lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     &lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     &lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     Такая реализация будет работать за O(n).&lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     &lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     ==Динамический convex hull trick==&lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     Заметим, что условия на прямые, что &amp;lt;tex&amp;gt;k[i]&amp;lt;/tex&amp;gt; возрастает/убывает и &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt; убывает/возрастает выглядят достаточно редкими для большинства задач. Пусть в задаче таких ограничений нет. Первый способ борьбы с этой проблемой {{---}} отсортировать входные данные нужным образом, не испортив свойств задачи (пример : задача G c Санкт-Петербургских сборов к РОИ 2016[http://neerc.ifmo.ru/school/camp-2016/problems/20160318a.pdf]).&lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      &lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      Но рассмотрим общий случай. По-прежнему у нас есть выпуклая оболочка прямых, имея которую мы за &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; можем найти   &amp;lt;tex&amp;gt;dp[i]&amp;lt;/tex&amp;gt;, но теперь вставку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-й прямой в оболочку уже нельзя выполнить описанным ранее способом за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt; в среднем. У нас есть выпуклая оболочка, наша прямая пересекает ее, возможно, «отсекая» несколько отрезков выпуклой оболочки в середине (рис. 4 : красная прямая {{---}} та, которую мы хотим вставить в наше множество). Более формально : теперь наша новая прямая будет ниже остальных при &amp;lt;tex&amp;gt;x \in [x1; x2]&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;x1, x2 \in R&amp;lt;/tex&amp;gt; {{---}} точки пересечения с некоторыми прямыми, причем &amp;lt;tex&amp;gt;x2&amp;lt;/tex&amp;gt; не обязательно равно &amp;lt;tex&amp;gt;+ \infty&amp;lt;/tex&amp;gt;&lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      [[Файл:picture4convexhull.png]]&lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      &lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      Чтобы уметь вставлять прямую в множество будем хранить &amp;lt;math&amp;gt;std::set&amp;lt;/math&amp;gt; (или любой аналог в других языках программирования) пар &amp;lt;tex&amp;gt;(k, st)&amp;lt;/tex&amp;gt; =  &amp;lt;tex&amp;gt;(коэффицент прямой, ее номер в глобальной нумерации)&amp;lt;/tex&amp;gt;. Когда приходит новая прямая, ищем последнюю прямую с меньшим угловым коэффицентом, чем у той прямой, которую мы хотим добавить в множество. Поиск такой прямой занимает &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt;. Начиная с найденной прямой выполняем &amp;quot;старый&amp;quot; алгоритм (удаляем, пока текущая прямая множества бесполезна). И симметричный алгоритм применяем ко всем прямым справа от нашей (удаляем правого соседа нашей прямой, пока она пересекает нас позже, чем своего правого соседа).&lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      &lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      Асимптотика решения составит &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; на каждый из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; запросов «добавить прямую» + &amp;lt;tex&amp;gt;O(n\cdot\log(n))&amp;lt;/tex&amp;gt; суммарно на удаление прямых, т.к. по-прежнему каждая прямая не более одного раза удалится из множества, а каждое удаление из std::set занимает &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; времени. Итого &amp;lt;math&amp;gt;O(n\cdot\log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      &lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      == Альтернативный подход ==&lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      Другой способ интерпретировать выражение &amp;lt;tex&amp;gt;dp[i] = \min\limits_{j=0...i-1}(c[j] \cdot a[i] + dp[j])&amp;lt;/tex&amp;gt;  заключается в том, что мы будем пытаться свести задачу к стандартной выпуклой оболочке множества точек. Перепишем выражение средующим образом : &amp;lt;tex&amp;gt;dp[j] + a[i] \cdot c[j] = (dp[j], c[j]) \cdot (1, a[i])&amp;lt;/tex&amp;gt;, т.е. запишем как скалярное произведение векторов &amp;lt;tex&amp;gt;v[j] = (dp[j], c[j])&amp;lt;/tex&amp;gt; и &amp;lt;tex &amp;gt;u[i] = (1, a[i])&amp;lt;/tex &amp;gt;. Вектора  &amp;lt;tex &amp;gt;v[j] =  (dp[j], c[j])&amp;lt;/tex&amp;gt; хотелось бы организовать так, чтобы за &amp;lt;tex &amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; находить вектор, максимизирующий выражение &amp;lt;tex&amp;gt;v[j] \cdot u[i]&amp;lt;/tex&amp;gt;. Посмотрим на рис. 5. Заметим интуитивно очевидный факт : красная точка (вектор) &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; не может давать более оптимальное значение &amp;lt;tex&amp;gt;v[j] \cdot u[i]&amp;lt;/tex&amp;gt; одновременно чем обе синие точки. По этой причине нам достаточно оставить выпуклую оболочку векторов &amp;lt;tex&amp;gt;v[j]&amp;lt;/tex&amp;gt;, а ответ на запрос {{---}} это поиск &amp;lt;tex&amp;gt;v[j]&amp;lt;/tex&amp;gt;, максимизирующего проекцию на &amp;lt;tex&amp;gt;u[i]&amp;lt;/tex&amp;gt;. Это задача поиска ближайшей точки выпуклого многоугольника (составленного из точек выпуклой оболочки) к заданной прямой (из &amp;lt;tex&amp;gt;(0, 0)&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;(1, a[i])&amp;lt;/tex&amp;gt;).  Ее можно решить за &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; двумя бинарными или одним тернарным поиском&lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      Асимптотика алгоритма по-прежнему составит &amp;lt;tex&amp;gt;O(n \cdot \log(n))&amp;lt;/tex&amp;gt;&lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      &lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      [[Файл:picture5convexhull.png]]&lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      &lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      Докажем то, что описанный выше алгоритм корректен. Для этого достаточно показать, что если имеются &amp;lt;math&amp;gt;3&amp;lt;/math&amp;gt; вектора &amp;lt;math&amp;gt;a, b, c&amp;lt;/math&amp;gt;, расположенные как на рис. 5, т.е. точка &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; не лежит на выпуклой оболочке векторов &amp;lt;tex&amp;gt;0, a, b, c &amp;lt;/tex&amp;gt; : &amp;lt;tex&amp;gt; \Leftrightarrow |[a-b, b-c]| &amp;lt; 0 &amp;lt;/tex&amp;gt;, то либо  &amp;lt;tex&amp;gt;(a, u[i])&amp;lt;/tex&amp;gt; оптимальнее, чем &amp;lt;tex&amp;gt;(b, u[i])&amp;lt;/tex&amp;gt;,  либо &amp;lt;tex&amp;gt;(c, u[i])&amp;lt;/tex&amp;gt; оптимальнее, чем &amp;lt;tex&amp;gt;(b, u[i])&amp;lt;/tex&amp;gt;.&lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      {{Теорема&lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |id=th12392. &lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |statement=Если есть &amp;lt;tex&amp;gt;3&amp;lt;/tex&amp;gt; вектора &amp;lt;tex&amp;gt;a, b, c&amp;lt;/tex&amp;gt;, таких что &amp;lt;tex&amp;gt;|[a-b, b-c]| &amp;lt; 0&amp;lt;/tex&amp;gt; то либо &amp;lt;math&amp;gt;(a, u) &amp;lt; (b, u)&amp;lt;/math&amp;gt;, либо &amp;lt;math&amp;gt;(c, u) &amp;lt; (b, u)&amp;lt;/math&amp;gt;, где вектор &amp;lt;math&amp;gt;u = (1; k)&amp;lt;/math&amp;gt;.&lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |proof=По условию теоремы известно, что &amp;lt;tex&amp;gt;|[a-b, b-c]| &amp;lt; 0 \Leftrightarrow (a_{x} - b_{x})\cdot(b_{y} - c_{y}) &amp;lt; (a_{y} - b_{y}) \cdot (b_{x} - c_{x})&amp;lt;/tex&amp;gt; (*). Предположим (от противного), что &amp;lt;tex&amp;gt;(b, u) &amp;lt; (a, u) \Leftrightarrow b_{x}  + k \cdot b_{y} &amp;lt; c_{x} + k \cdot c_{y} \Leftrightarrow (b_{x} - c_{x}) &amp;lt; k \cdot (c_{y} - b_{y})&amp;lt;/tex&amp;gt; и  &amp;lt;tex&amp;gt;(b, u) &amp;lt; (c, u) \Leftrightarrow b_{x}  + k \cdot b_{y} &amp;lt; a_{x} + k \cdot a_{y} \Leftrightarrow (a_{x} - b_{x}) &amp;gt; k \cdot (b_{y} - a_{y})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      &lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      Подставим эти неравенства в (*). Получим цепочку неравенств : &amp;lt;tex&amp;gt;k \cdot (a_{y} - b_{y})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (c_{y} - b_{y}) = k&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (b_{y} - a_{y}) \cdot &amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt;(b_{y} - c_{y})&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt; &amp;lt; (a_{x} - b_{x})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (b_{y} - c_{y})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; &amp;lt; (a_{y} - b_{y}) \cdot &amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt;(b_{x} - c_{x})&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;&amp;lt; k \cdot (a_{y} - b_{y})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (c_{y} - b_{y})&amp;lt;/tex&amp;gt;. Получили противоречие : &amp;lt;tex&amp;gt;k \cdot (a_{y} - b_{y}) \cdot (c_{y} - b_{y}) &amp;lt; k \cdot (a_{y} - b_{y}) \cdot (c_{y} - b_{y})&amp;lt;/tex&amp;gt;. Значит предположение неверно, чтд.&lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      }}&lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      &lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      Из доказанной теоремы и следует корректность алгоритма.&lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      &lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      ==См. также==&lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      1) http://neerc.ifmo.ru/wiki/index.php?title=Статические_выпуклые_оболочки:_Джарвис,_Грэхем,_Эндрю,_Чен,_QuickHull&lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      &lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      2) http://neerc.ifmo.ru/wiki/index.php?title=Динамическое_программирование&lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      &lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      [[Категория:Дискретная математика и алгоритмы]]&lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      [[Категория: Динамическое программирование]]&lt;br /&gt;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      [[Категория: Способы оптимизации методов динамического программирования]]&lt;/div&gt;</summary>
		<author><name>92.255.113.52</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=Convex_hull_trick&amp;diff=60035</id>
		<title>Convex hull trick</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=Convex_hull_trick&amp;diff=60035"/>
				<updated>2017-01-18T18:14:02Z</updated>
		
		<summary type="html">&lt;p&gt;92.255.113.52: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Convex hull trick -- один из методов оптимизации динамического программирования [[http://neerc.ifmo.ru/wiki/index.php?title=Динамическое_программирование]], использующий идею выпуклой оболочки. Позволяет улучшить ассимптотику решения некоторых задачь, решемых методом динамического программирования с &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt; до &amp;lt;tex&amp;gt;O(n\cdot\log(n))&amp;lt;/tex&amp;gt;. Техника впервые появилась в 1995 году (задачу на нее предложили в USACO -- национальной олимпиаде США по программированию). Массовую известность получила после IOI (международной олимпиады по программированию для школьников) 2002.&lt;br /&gt;
&lt;br /&gt;
==Пример задачи, решаемой методом convex hull trick==&lt;br /&gt;
Рассмотрим задачу на ДП:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; деревьев с высотами &amp;lt;tex&amp;gt;a_1, a_2, \dots, a_n&amp;lt;/tex&amp;gt; (в метрах). Требуется спилить их все, потратив минимальное количество монет на заправку&lt;br /&gt;
бензопилы. Но пила устроена так, что она может спиливать только по 1 метру от дерева, к которому ее применили. Также после&lt;br /&gt;
срубленного метра (любого дерева) пилу нужно заправлять, платя за  бензин определенной кол-во монет. Причем стоимость &lt;br /&gt;
бензина зависит от срубленных (полностью) деревьев. Если сейчас максимальный индекс срубленного дерева равен &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, то цена заправки &lt;br /&gt;
равна &amp;lt;tex&amp;gt;c_i&amp;lt;/tex&amp;gt;.  Изначально пила заправлена.&lt;br /&gt;
Также известны следующие ограничения : &amp;lt;tex&amp;gt;c_n = 0, a_1 = 1, a_i&amp;lt;/tex&amp;gt; возрастают, &amp;lt;tex&amp;gt;c_i&amp;lt;/tex&amp;gt; убывают. Изначально пила заправлена.&lt;br /&gt;
(убывание и возрастание нестрогие)&lt;br /&gt;
}}&lt;br /&gt;
(Задача H с Санкт-Петербургских сборов к РОИ 2016[http://neerc.ifmo.ru/school/camp-2016/problems/20160318a.pdf])&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
&amp;lt;includeonly&amp;gt;{{#if: {{{neat|}}}|&lt;br /&gt;
&amp;lt;div style=&amp;quot;background-color: #fcfcfc; float:left;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;background-color: #ddd;&amp;quot;&amp;gt;'''Задача:'''&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;border:1px dashed #2f6fab; padding: 8px; font-style: italic;&amp;quot;&amp;gt;{{{definition}}}&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;|&lt;br /&gt;
&amp;lt;table border=&amp;quot;0&amp;quot; width=&amp;quot;100%&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td style=&amp;quot;background-color: #ddd&amp;quot;&amp;gt;'''Задача:'''&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td style=&amp;quot;border:1px dashed #2f6fab; padding: 8px; background-color: #fcfcfc; font-style: italic;&amp;quot;&amp;gt;{{{definition}}}&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;}}&lt;br /&gt;
&amp;lt;/includeonly&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Наивное решение==&lt;br /&gt;
Сначала заметим важный факт : т.к. &amp;lt;tex&amp;gt;c[i]&amp;lt;/tex&amp;gt; убывают (нестрого) и &amp;lt;tex&amp;gt;c[n] = 0&amp;lt;/tex&amp;gt;, то все &amp;lt;tex&amp;gt;c[i]&amp;lt;/tex&amp;gt; неотрицательны.&lt;br /&gt;
Понятно, что нужно затратив минимальную стоимость срубить последнее (&amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;-е) дерево, т.к. после него все деревья можно будет рубить бесплатно (т.к. &amp;lt;tex&amp;gt;c[n] = 0&amp;lt;/tex&amp;gt;). Посчитаем следующую динамику : &amp;lt;tex&amp;gt;dp[i]&amp;lt;/tex&amp;gt; -- минимальная стоимость, заплатив которую можно добиться того, что дерево номер &amp;lt;tex&amp;gt;i.&amp;lt;/tex&amp;gt; будет срублено.&lt;br /&gt;
База динамики : &amp;lt;tex&amp;gt;dp[1] = 0&amp;lt;/tex&amp;gt;, т.к. изначально пила заправлена и высота первого дерева равна 1, по условию задачи.&lt;br /&gt;
Переход динамики :  понятно, что выгодно рубить сначала более дорогие и низкие деревья, а потом более высокие и дешевые (док-во этого факта оставляется читателям как несложное упражнение, т.к. эта идея относится скорее к теме жадных алгоритмнов, чем к теме данной статьи). Поэтому перед  &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-м деревом мы обязательно срубили какое-то &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-е, причем &amp;lt;tex&amp;gt;j \leqslant i - 1&amp;lt;/tex&amp;gt;. Поэтому чтобы найти &amp;lt;tex&amp;gt;dp[i]&amp;lt;/tex&amp;gt; нужно перебрать все &amp;lt;tex&amp;gt;1 \leqslant j \leqslant i - 1&amp;lt;/tex&amp;gt; и попытаться использовать ответ для дерева намер &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;. Итак, пусть перед &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-м деревом мы полностью срубили &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-е, причем высота &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева составляет &amp;lt;tex&amp;gt;a[i]&amp;lt;/tex&amp;gt;, а т.к. последнее дерево, которое мы срубили имеет индекс &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, то стоимость каждого метра &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева составит &amp;lt;tex&amp;gt;c[j]&amp;lt;/tex&amp;gt;.  Поэтому на сруб &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева мы потратим &amp;lt;tex&amp;gt;a[i] \cdot c[j]&amp;lt;/tex&amp;gt; монет. Также не стоит забывать, ситуацию, когда  &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-е дерево полностью срублено, мы получили не бесплатно, а за &amp;lt;tex&amp;gt;dp[j]&amp;lt;/tex&amp;gt; монет.&lt;br /&gt;
Итогвая формула пересчета : &amp;lt;tex&amp;gt;dp[i] = \min\limits_{j=1...i-1} (dp[j] + a[i] \cdot c[j])&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Посмотрим на код выше описанного решения:&lt;br /&gt;
 '''int''' &amp;lt;tex&amp;gt;\mathtt{simpleDP}&amp;lt;/tex&amp;gt;('''int''' a[n], '''int''' c[n])&lt;br /&gt;
    dp[1] = 0&lt;br /&gt;
    dp[2] = dp[3] = ... = dp[n] = &amp;lt;tex&amp;gt;\infty&amp;lt;/tex&amp;gt;&lt;br /&gt;
    '''for'''  i = 1..n-1  &lt;br /&gt;
       dp[i] = &amp;lt;tex&amp;gt;+\infty&amp;lt;/tex&amp;gt;&lt;br /&gt;
       '''for''' j = 0..i-1 &lt;br /&gt;
           '''if''' (dp[j] + a[i] &amp;lt;tex&amp;gt;\cdot&amp;lt;/tex&amp;gt; c[j] &amp;lt; dp[i])&lt;br /&gt;
           dp[i] = dp[j] + a[i] &amp;lt;tex&amp;gt;\cdot&amp;lt;/tex&amp;gt; c[j]&lt;br /&gt;
 '''return''' dp[n]&lt;br /&gt;
Нетрудно видеть, что такая динамика работает за &amp;lt;tex&amp;gt;O(n^2)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Ключевая идея оптимизации==&lt;br /&gt;
Для начала сделаем замену обозначений. Давайте обозначим &amp;lt;tex&amp;gt;dp[j]&amp;lt;/tex&amp;gt; за &amp;lt;tex&amp;gt;b[j]&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;a[i]&amp;lt;/tex&amp;gt; за &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt;, а &amp;lt;tex&amp;gt;c[j]&amp;lt;/tex&amp;gt; за &amp;lt;tex&amp;gt;k[j]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь формула приняла вид &amp;lt;tex&amp;gt;dp[i] = \min\limits_{j=0...i-1}(k[j] \cdot x[i] + b[j])&amp;lt;/tex&amp;gt;. Выражение &amp;lt;tex&amp;gt;k[j] \cdot x + b[j]&amp;lt;/tex&amp;gt; -- это в точности уравнение прямой вида &amp;lt;tex&amp;gt;y = kx + b&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сопоставим каждому &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, обработанному ранее, прямую &amp;lt;tex&amp;gt;y[j](x) = k[j] \cdot x + b[j]&amp;lt;/tex&amp;gt;. Из условия «&amp;lt;tex&amp;gt;c[i]&amp;lt;/tex&amp;gt; убывают &amp;lt;tex&amp;gt;\Leftrightarrow  k[j]&amp;lt;/tex&amp;gt; уменьшаются с номером &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;» следует то, что прямые, полученные ранее отсортированы в порядке убывания углового коэффициент. Давайте нарисуем несколько таких прямых :&lt;br /&gt;
&lt;br /&gt;
[[Файл:picture1convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
Выделим множество точек &amp;lt;tex&amp;gt;(x0, y0)&amp;lt;/tex&amp;gt; , таких что все они принадлежат одной из прямых и при этом нету ни одной прямой &amp;lt;tex&amp;gt;y’(x)&amp;lt;/tex&amp;gt;, такой что &amp;lt;tex&amp;gt;y’(x0) &amp;lt; y0&amp;lt;/tex&amp;gt;. Иными словами возьмем «выпуклую (вверх) оболочку» нашего множества прямых (её еще называют нижней ошибающей множества прямых на плоскости). Назовем ее «&amp;lt;tex&amp;gt;y = convex(x)&amp;lt;/tex&amp;gt;». Видно, что множество точек &amp;lt;math&amp;gt;(x, convex(x))&amp;lt;/math&amp;gt; представляет собой выпуклую вверх функцию.&lt;br /&gt;
&lt;br /&gt;
==Цель нижней огибающей множества прямых==&lt;br /&gt;
Пусть мы считаем динамику для &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева. Его задает &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt;. Итак, нам нужно для данного &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt; найти &amp;lt;tex&amp;gt;\min\limits_{j=0..i-1}(k[j] \cdot x[i] + b[i]) = \min\limits_{j=0..i-1}(y[j](x[i]))&amp;lt;/tex&amp;gt;. Это выражение есть &amp;lt;math&amp;gt;convex(x[i])&amp;lt;/math&amp;gt;. Из монотонности угловых коэффицентов отрезков, задающих выпуклую оболочку, и их расположения по координаты x следует то, что отрезок, который пересекает прямую &amp;lt;tex&amp;gt;x = x[i]&amp;lt;/tex&amp;gt;, можно найти бинарным поиском. Это потребует &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; времени на поиск такого &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;dp[i] = k[j] \cdot x[i] + b[j]&amp;lt;/tex&amp;gt;. Теперь осталось научиться поддерживать множество прямых и быстро добавлять &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-ю прямую после того, как мы посчитали &amp;lt;tex&amp;gt;b[i] = dp[i]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Воспользуемся идеей алгоритма построения выпуклой оболочки множества точек. Заведем 2 стека &amp;lt;tex&amp;gt;k[]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;b[]&amp;lt;/tex&amp;gt;, которые задают прямые в отсортированном порядке их угловыми коэффицентами и свободными членами. Рассмотрим ситуацию, когда мы хотим добавить новую (&amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-тую) прямую в множество. Пусть сейчас в множестве лежит &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt; прямых (нумерация с 1). Пусть &amp;lt;tex&amp;gt;(xL, yL)&amp;lt;/tex&amp;gt; -- точка пересечения &amp;lt;tex&amp;gt;sz - 1&amp;lt;/tex&amp;gt;-й прямой множества и &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-й, а &amp;lt;tex&amp;gt;(xR, yR)&amp;lt;/tex&amp;gt; -- точка пересечения новой прямой, которую мы хотим добавить в конец множества и &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-й. Нас будут интересовать только их &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;-овые координаты &amp;lt;tex&amp;gt;xL&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;xR&amp;lt;/tex&amp;gt;, соответственно. Если оказалось, что новая прямая пересекает &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-ю прямую выпуклой оболочки позже, чем &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-я &amp;lt;tex&amp;gt;sz - 1&amp;lt;/tex&amp;gt;-ю, т.е. &amp;lt;tex&amp;gt;(xL \geqslant xR)&amp;lt;/tex&amp;gt;, то &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-ю удалим из нашего множества, иначе -- остановимся. Так будем делать, пока либо кол-во прямых в стеке не станет равным 2, либо &amp;lt;tex&amp;gt;xL&amp;lt;/tex&amp;gt; не станет меньше &amp;lt;tex&amp;gt;xR.&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Асимптотика : аналогично обычному алгоритму построения выпуклой оболочки, каждая прямая ровно &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; раз добавится в стек и максимум &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; раз удалится. Значит время работы перестройки выпуклой оболочки займет &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; суммарно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:picture2convexhull.png]]&lt;br /&gt;
[[Файл:picture3convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
{{Теорема&lt;br /&gt;
|id=th1239. &lt;br /&gt;
|statement=Алгоритм построения нижней огибающей множества прямых корректен.&lt;br /&gt;
|proof=Достаточно показать, что последнюю прямую нужно удалить из множества т.и т.т., когда она наша новая прямая пересекает ее в точке с координатой по оси X, меньшей, чем последняя -- предпоследнюю.&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;Y(x) = Kx + B&amp;lt;/tex&amp;gt; -- уравнение новой прямой,  &amp;lt;tex&amp;gt;y[i](x) = K[i]x + B[i]&amp;lt;/tex&amp;gt; -- уравнения прямых множества. Тогда т.к. &amp;lt;tex&amp;gt;K &amp;lt; K[sz]&amp;lt;/tex&amp;gt;, то при &amp;lt;tex&amp;gt;x \in [- \infty; xR]  : y[sz](x) &amp;lt;= Y(x)&amp;lt;/tex&amp;gt;, а т.к. &amp;lt;tex&amp;gt; K[sz] &amp;lt; K[sz - 1]&amp;lt;/tex&amp;gt;, то при &amp;lt;tex&amp;gt;x \in [xL; + \infty]  : y[sz - 1](x) \geqslant y[sz](x)&amp;lt;/tex&amp;gt;. Если &amp;lt;tex&amp;gt;xL &amp;lt; xR&amp;lt;/tex&amp;gt;, то при &amp;lt;tex&amp;gt;x \in [xL; xR]  : y[sz - 1] \geqslant y[sz](x) и Y(x) \geqslant y[sz](x)&amp;lt;/tex&amp;gt;, т.е. на отрезке &amp;lt;tex&amp;gt;[xL; xR]&amp;lt;/tex&amp;gt; прямая номер sz лежит ниже остальных и её нужно оставить в множестве. Если же &amp;lt;tex&amp;gt;xL &amp;gt; xR&amp;lt;/tex&amp;gt;, то она ниже всех на отрезке &amp;lt;tex&amp;gt;[xL; xR] = \varnothing &amp;lt;/tex&amp;gt;, т.е. её можно удалить из множества&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==Детали реализации:==&lt;br /&gt;
Будем хранить 2 массива : &amp;lt;tex&amp;gt;front[]&amp;lt;/tex&amp;gt; -- &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;-координаты, начиная с которых прямые совпадают с выпуклой оболочкой (т.е. i-я прямая совпадает с выпуклой оболочкой текущего множества прямых при &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;[front[i]; front[i + 1])&amp;lt;/tex&amp;gt; ) и &amp;lt;tex&amp;gt;st[]&amp;lt;/tex&amp;gt; -- номера деревьев, соответствующих прямым (т.е. &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-я прямая множества, где &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;[1; sz]&amp;lt;/tex&amp;gt; соответствует дереву номер &amp;lt;tex&amp;gt;sz[i]&amp;lt;/tex&amp;gt;). Также воспользуемся тем, что &amp;lt;tex&amp;gt;x[i] = a[i]&amp;lt;/tex&amp;gt; возрастают (по условию задачи), а значит мы можем искать первое такое &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;x[i] \geqslant front[j]&amp;lt;/tex&amp;gt; не бинарным поиском, а методом двух указателей за &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; операций суммарно. Также массив front[] можно хранить в целых числах, округляя х-координаты в сторону лежащих правее по оси x до ближайшего целого (*), т.к. на самом деле мы, считая динамику, подставляем в уравнения прямых только целые &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt;, а значит если &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt;-я прямая пересекается с &amp;lt;tex&amp;gt;k+1&amp;lt;/tex&amp;gt;-й в точке &amp;lt;tex&amp;gt;z +&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\alpha&amp;lt;/tex&amp;gt; (&amp;lt;math&amp;gt;z&amp;lt;/math&amp;gt;-целое,  &amp;lt;tex&amp;gt;\alpha&amp;lt;/tex&amp;gt;  &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;[0;1)&amp;lt;/tex&amp;gt;), то мы будем подставлять в их уравнения &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt;  или &amp;lt;tex&amp;gt;z + 1&amp;lt;/tex&amp;gt;. Поэтому можно считать, что новая прямая начинает совпадать с выпуклой оболочкой, начиная с &amp;lt;tex&amp;gt;x = z+1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Реализация==&lt;br /&gt;
  '''int''' &amp;lt;tex&amp;gt;\mathtt{ConvexHullTrick}&amp;lt;/tex&amp;gt;('''int''' a[n], '''int''' c[n])&lt;br /&gt;
     st[1] = 1&lt;br /&gt;
     from[1] = -&amp;lt;tex&amp;gt;\infty&amp;lt;/tex&amp;gt;&amp;lt;font color=green&amp;gt;// первая прямая покрывает все x-ы, начиная с -∞ &amp;lt;/font&amp;gt;&lt;br /&gt;
     sz = 1 &amp;lt;font color=green&amp;gt;// текущий размер выпуклой оболочки &amp;lt;/font&amp;gt;&lt;br /&gt;
     pos = 1 &amp;lt;font color=green&amp;gt;// текущая позиция первого такого j, что x[i] \geqslant front[st[j]] &amp;lt;/font &amp;gt;&lt;br /&gt;
     '''for'''  i = 2..n  &lt;br /&gt;
          '''while''' (front[pos] &amp;lt; x[i]) &amp;lt;font color=green&amp;gt;// метод 1 указателя (ищем первое pos, такое что x[i] покрывается &amp;quot;областью действия&amp;quot; st[pos]-той прямой &amp;lt;/font &amp;gt;&lt;br /&gt;
               pos = pos + 1&lt;br /&gt;
          j = st[pos]&lt;br /&gt;
          dp[i] = K[j]&amp;lt;math&amp;gt;\cdot&amp;lt;/math&amp;gt;a[i] + B[j] &lt;br /&gt;
          '''if''' (i &amp;lt; n)   &amp;lt;font color=green&amp;gt;// если у нас добавляется НЕ последняя прямая, то придется пересчитать выпуклую оболочку &amp;lt;/font &amp;gt;&lt;br /&gt;
                K[i] = c[i]  &amp;lt;font color=green&amp;gt;// наши переобозначения переменных &amp;lt;/font &amp;gt;&lt;br /&gt;
                B[i] = dp[i] &amp;lt;font color=green&amp;gt;// наши переобозначения переменных &amp;lt;/font &amp;gt;&lt;br /&gt;
                x = -&amp;lt;tex&amp;gt;\infty&amp;lt;/tex&amp;gt; &lt;br /&gt;
                '''while''' ''true'' &lt;br /&gt;
                      j = st[sz]&lt;br /&gt;
                      x = divide(B[j] - B[i], K[i] - K[j]) &amp;lt;font color=green&amp;gt;// x-координата пересечения с последней прямой оболочки, округленное в нужную сторону (*) &amp;lt;/font &amp;gt;&lt;br /&gt;
                      '''if''' (x &amp;gt; from[sz]) '''break'''  &amp;lt;font color=green&amp;gt;// перестаем удалять последнюю прямую из множества, если новая прямая пересекает ее позже, чем начинается ее &amp;quot;область действия&amp;quot; &amp;lt;/font &amp;gt;&lt;br /&gt;
                      sz = sz - 1&amp;lt;font color=green&amp;gt;// удаляем последнюю прямую, если она лишняя &amp;lt;/font &amp;gt;&lt;br /&gt;
                 st[sz + 1] = i  &lt;br /&gt;
                 from[sz + 1] = x &amp;lt;font color=green&amp;gt;// добавили новую прямую &amp;lt;/font &amp;gt;&lt;br /&gt;
                 sz = sz + 1&lt;br /&gt;
  '''return''' dp[n] &lt;br /&gt;
Здесь функция &amp;lt;tex&amp;gt;\mathtt{divide}&amp;lt;/tex&amp;gt;(a, b) возвращает нужное(*) округление a / b. Приведем её код :&lt;br /&gt;
 '''int''' &amp;lt;tex&amp;gt;\mathtt{divide}&amp;lt;/tex&amp;gt;('''int''' a, '''int''' b)&lt;br /&gt;
        delta = 0&lt;br /&gt;
        '''if''' (a '''mod''' b ≠ 0) delta = 1&lt;br /&gt;
        '''if''' ((a &amp;gt; 0 '''and''' b &amp;gt; 0) '''or''' (a &amp;lt; 0 '''and''' b &amp;lt; 0)) '''return''' [a / b] + delta&lt;br /&gt;
 '''return''' -[|a| / |b|]&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
Такая реализация будет работать за O(n).&lt;br /&gt;
&lt;br /&gt;
==Динамический convex hull trick==&lt;br /&gt;
	Заметим, что условия на прямые, что &amp;lt;tex&amp;gt;k[i]&amp;lt;/tex&amp;gt; возрастает/убывает и &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt; убывает/возрастает выглядят достаточно редкими для большинства задач. Пусть в задаче таких ограничений нет. Первый способ борьбы с этой проблемой -- отсортировать входные данные нужным образом, не испортив свойств задачи (пример : задача G c Санкт-Петербургских сборов к РОИ 2016[http://neerc.ifmo.ru/school/camp-2016/problems/20160318a.pdf]).&lt;br /&gt;
&lt;br /&gt;
	Но рассмотрим общий случай. По-прежнему у нас есть выпуклая оболочка прямых, имея которую мы за &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; можем найти   &amp;lt;tex&amp;gt;dp[i]&amp;lt;/tex&amp;gt;, но теперь вставку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-й прямой в оболочку уже нельзя выполнить описанным ранее способом за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt; в среднем. У нас есть выпуклая оболочка, наша прямая пересекает ее, возможно, «отсекая» несколько отрезков выпуклой оболочки в середине (рис. 4 : красная прямая -- та, которую мы хотим вставить в наше множество). Более формально : теперь наша новая прямая будет ниже остальных при &amp;lt;tex&amp;gt;x \in [x1; x2]&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;x1, x2 \in R&amp;lt;/tex&amp;gt; -- точки пересечения с некоторыми прямыми, причем &amp;lt;tex&amp;gt;x2&amp;lt;/tex&amp;gt; не обязательно равно &amp;lt;tex&amp;gt;+ \infty&amp;lt;/tex&amp;gt;&lt;br /&gt;
[[Файл:picture4convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
Чтобы уметь вставлять прямую в множество будем хранить &amp;lt;math&amp;gt;std::set&amp;lt;/math&amp;gt; (или любой аналог в других языках программирования) пар &amp;lt;tex&amp;gt;(k, st)&amp;lt;/tex&amp;gt; =  &amp;lt;tex&amp;gt;(коэффицент прямой, ее номер в глобальной нумерации)&amp;lt;/tex&amp;gt;. Когда приходит новая прямая, ищем последнюю прямую с меньшим угловым коэффицентом, чем у той прямой, которую мы хотим добавить в множество. Поиск такой прямой занимает &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt;. Начиная с найденной прямой выполняем &amp;quot;старый&amp;quot; алгоритм (удаляем, пока текущая прямая множества бесполезна). И симметричный алгоритм применяем ко всем прямым справа от нашей (удаляем правого соседа нашей прямой, пока она пересекает нас позже, чем своего правого соседа).&lt;br /&gt;
	&lt;br /&gt;
Асимптотика решения составит &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; на каждый из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; запросов «добавить прямую» + &amp;lt;tex&amp;gt;O(n\cdot\log(n))&amp;lt;/tex&amp;gt; суммарно на удаление прямых, т.к. по-прежнему каждая прямая не более одного раза удалится из множества, а каждое удаление из std::set занимает &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; времени. Итого &amp;lt;math&amp;gt;O(n\cdot\log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Альтернативный подход ==&lt;br /&gt;
Другой способ интерпретировать выражение &amp;lt;tex&amp;gt;dp[i] = \min\limits_{j=0...i-1}(c[j] \cdot a[i] + dp[j])&amp;lt;/tex&amp;gt;  заключается в том, что мы будем пытаться свести задачу к стандартной выпуклой оболочке множества точек. Перепишем выражение средующим образом : &amp;lt;tex&amp;gt;dp[j] + a[i] \cdot c[j] = (dp[j], c[j]) \cdot (1, a[i])&amp;lt;/tex&amp;gt;, т.е. запишем как скалярное произведение векторов &amp;lt;tex&amp;gt;v[j] = (dp[j], c[j])&amp;lt;/tex&amp;gt; и &amp;lt;tex &amp;gt;u[i] = (1, a[i])&amp;lt;/tex &amp;gt;. Вектора  &amp;lt;tex &amp;gt;v[j] =  (dp[j], c[j])&amp;lt;/tex&amp;gt; хотелось бы организовать так, чтобы за &amp;lt;tex &amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; находить вектор, максимизирующий выражение &amp;lt;tex&amp;gt;v[j] \cdot u[i]&amp;lt;/tex&amp;gt;. Посмотрим на рис. 5. Заметим интуитивно очевидный факт : красная точка (вектор) &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; не может давать более оптимальное значение &amp;lt;tex&amp;gt;v[j] \cdot u[i]&amp;lt;/tex&amp;gt; одновременно чем обе синие точки. По этой причине нам достаточно оставить выпуклую оболочку векторов &amp;lt;tex&amp;gt;v[j]&amp;lt;/tex&amp;gt;, а ответ на запрос -- это поиск &amp;lt;tex&amp;gt;v[j]&amp;lt;/tex&amp;gt;, максимизирующего проекцию на &amp;lt;tex&amp;gt;u[i]&amp;lt;/tex&amp;gt;. Это задача поиска ближайшей точки выпуклого многоугольника (составленного из точек выпуклой оболочки) к заданной прямой (из &amp;lt;tex&amp;gt;(0, 0)&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;(1, a[i])&amp;lt;/tex&amp;gt;).  Ее можно решить за &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; двумя бинарными или одним тернарным поиском&lt;br /&gt;
Асимптотика алгоритма по-прежнему составит &amp;lt;tex&amp;gt;O(n \cdot \log(n))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Файл:picture5convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
Докажем то, что описанный выше алгоритм корректен. Для этого достаточно показать, что если имеются &amp;lt;math&amp;gt;3&amp;lt;/math&amp;gt; вектора &amp;lt;math&amp;gt;a, b, c&amp;lt;/math&amp;gt;, расположенные как на рис. 5, т.е. точка &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; не лежит на выпуклой оболочке векторов &amp;lt;tex&amp;gt;0, a, b, c &amp;lt;/tex&amp;gt; : &amp;lt;tex&amp;gt; \Leftrightarrow |[a-b, b-c]| &amp;lt; 0 &amp;lt;/tex&amp;gt;, то либо  &amp;lt;tex&amp;gt;(a, u[i])&amp;lt;/tex&amp;gt; оптимальнее, чем &amp;lt;tex&amp;gt;(b, u[i])&amp;lt;/tex&amp;gt;,  либо &amp;lt;tex&amp;gt;(c, u[i])&amp;lt;/tex&amp;gt; оптимальнее, чем &amp;lt;tex&amp;gt;(b, u[i])&amp;lt;/tex&amp;gt;.&lt;br /&gt;
{{Теорема&lt;br /&gt;
|id=th12392. &lt;br /&gt;
|statement=Если есть &amp;lt;tex&amp;gt;3&amp;lt;/tex&amp;gt; вектора &amp;lt;tex&amp;gt;a, b, c&amp;lt;/tex&amp;gt;, таких что &amp;lt;tex&amp;gt;|[a-b, b-c]| &amp;lt; 0&amp;lt;/tex&amp;gt; то либо &amp;lt;math&amp;gt;(a, u) &amp;lt; (b, u)&amp;lt;/math&amp;gt;, либо &amp;lt;math&amp;gt;(c, u) &amp;lt; (b, u)&amp;lt;/math&amp;gt;, где вектор &amp;lt;math&amp;gt;u = (1; k)&amp;lt;/math&amp;gt;.&lt;br /&gt;
|proof=По условию теоремы известно, что &amp;lt;tex&amp;gt;|[a-b, b-c]| &amp;lt; 0 \Leftrightarrow (a_{x} - b_{x})\cdot(b_{y} - c_{y}) &amp;lt; (a_{y} - b_{y}) \cdot (b_{x} - c_{x})&amp;lt;/tex&amp;gt; (*). Предположим (от противного), что &amp;lt;tex&amp;gt;(b, u) &amp;lt; (a, u) \Leftrightarrow b_{x}  + k \cdot b_{y} &amp;lt; c_{x} + k \cdot c_{y} \Leftrightarrow (b_{x} - c_{x}) &amp;lt; k \cdot (c_{y} - b_{y})&amp;lt;/tex&amp;gt; и  &amp;lt;tex&amp;gt;(b, u) &amp;lt; (c, u) \Leftrightarrow b_{x}  + k \cdot b_{y} &amp;lt; a_{x} + k \cdot a_{y} \Leftrightarrow (a_{x} - b_{x}) &amp;gt; k \cdot (b_{y} - a_{y})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Подставим эти неравенства в (*). Получим цепочку неравенств : &amp;lt;tex&amp;gt;k \cdot (a_{y} - b_{y})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (c_{y} - b_{y}) = k&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (b_{y} - a_{y}) \cdot &amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt;(b_{y} - c_{y})&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt; &amp;lt; (a_{x} - b_{x})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (b_{y} - c_{y})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; &amp;lt; (a_{y} - b_{y}) \cdot &amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt;(b_{x} - c_{x})&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;&amp;lt; k \cdot (a_{y} - b_{y})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (c_{y} - b_{y})&amp;lt;/tex&amp;gt;. Получили противоречие : &amp;lt;tex&amp;gt;k \cdot (a_{y} - b_{y}) \cdot (c_{y} - b_{y}) &amp;lt; k \cdot (a_{y} - b_{y}) \cdot (c_{y} - b_{y})&amp;lt;/tex&amp;gt;. Значит предположение неверно, чтд.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Из доказанной теоремы и следует корректность алгоритма.&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
1) http://neerc.ifmo.ru/wiki/index.php?title=Статические_выпуклые_оболочки:_Джарвис,_Грэхем,_Эндрю,_Чен,_QuickHull&lt;br /&gt;
&lt;br /&gt;
2) http://neerc.ifmo.ru/wiki/index.php?title=Динамическое_программирование&lt;br /&gt;
&lt;br /&gt;
[[Категория:Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория: Динамическое программирование]]&lt;br /&gt;
[[Категория: Способы оптимизации методов динамического программирования]]&lt;/div&gt;</summary>
		<author><name>92.255.113.52</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=Convex_hull_trick&amp;diff=60034</id>
		<title>Convex hull trick</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=Convex_hull_trick&amp;diff=60034"/>
				<updated>2017-01-18T18:11:43Z</updated>
		
		<summary type="html">&lt;p&gt;92.255.113.52: /* Наивное решение */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Convex hull trick -- один из методов оптимизации динамического программирования [[http://neerc.ifmo.ru/wiki/index.php?title=Динамическое_программирование]], использующий идею выпуклой оболочки. Позволяет улучшить ассимптотику решения некоторых задачь, решемых методом динамического программирования с &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt; до &amp;lt;tex&amp;gt;O(n\cdot\log(n))&amp;lt;/tex&amp;gt;. Техника впервые появилась в 1995 году (задачу на нее предложили в USACO -- национальной олимпиаде США по программированию). Массовую известность получила после IOI (международной олимпиады по программированию для школьников) 2002.&lt;br /&gt;
&lt;br /&gt;
==Пример задачи, решаемой методом convex hull trick==&lt;br /&gt;
Рассмотрим задачу на ДП:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; деревьев с высотами &amp;lt;tex&amp;gt;a_1, a_2, \dots, a_n&amp;lt;/tex&amp;gt; (в метрах). Требуется спилить их все, потратив минимальное количество монет на заправку&lt;br /&gt;
бензопилы. Но пила устроена так, что она может спиливать только по 1 метру от дерева, к которому ее применили. Также после&lt;br /&gt;
срубленного метра (любого дерева) пилу нужно заправлять, платя за  бензин определенной кол-во монет. Причем стоимость &lt;br /&gt;
бензина зависит от срубленных (полностью) деревьев. Если сейчас максимальный индекс срубленного дерева равен &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, то цена заправки &lt;br /&gt;
равна &amp;lt;tex&amp;gt;c_i&amp;lt;/tex&amp;gt;.  Изначально пила заправлена.&lt;br /&gt;
Также известны следующие ограничения : &amp;lt;tex&amp;gt;c_n = 0, a_1 = 1, a_i&amp;lt;/tex&amp;gt; возрастают, &amp;lt;tex&amp;gt;c_i&amp;lt;/tex&amp;gt; убывают. Изначально пила заправлена.&lt;br /&gt;
(убывание и возрастание нестрогие)&lt;br /&gt;
}}&lt;br /&gt;
(Задача H с Санкт-Петербургских сборов к РОИ 2016[http://neerc.ifmo.ru/school/camp-2016/problems/20160318a.pdf])&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
&amp;lt;includeonly&amp;gt;{{#if: {{{neat|}}}|&lt;br /&gt;
&amp;lt;div style=&amp;quot;background-color: #fcfcfc; float:left;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;background-color: #ddd;&amp;quot;&amp;gt;'''Задача:'''&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;border:1px dashed #2f6fab; padding: 8px; font-style: italic;&amp;quot;&amp;gt;{{{definition}}}&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;|&lt;br /&gt;
&amp;lt;table border=&amp;quot;0&amp;quot; width=&amp;quot;100%&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td style=&amp;quot;background-color: #ddd&amp;quot;&amp;gt;'''Задача:'''&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td style=&amp;quot;border:1px dashed #2f6fab; padding: 8px; background-color: #fcfcfc; font-style: italic;&amp;quot;&amp;gt;{{{definition}}}&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;}}&lt;br /&gt;
&amp;lt;/includeonly&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Наивное решение==&lt;br /&gt;
Сначала заметим важный факт : т.к. &amp;lt;tex&amp;gt;c[i]&amp;lt;/tex&amp;gt; убывают (нестрого) и &amp;lt;tex&amp;gt;c[n] = 0&amp;lt;/tex&amp;gt;, то все &amp;lt;tex&amp;gt;c[i]&amp;lt;/tex&amp;gt; неотрицательны.&lt;br /&gt;
Понятно, что нужно затратив минимальную стоимость срубить последнее (&amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;-е) дерево, т.к. после него все деревья можно будет рубить бесплатно (т.к. &amp;lt;tex&amp;gt;c[n] = 0&amp;lt;/tex&amp;gt;). Посчитаем следующую динамику : &amp;lt;tex&amp;gt;dp[i]&amp;lt;/tex&amp;gt; -- минимальная стоимость, заплатив которую можно добиться того, что дерево номер &amp;lt;tex&amp;gt;i.&amp;lt;/tex&amp;gt; будет срублено.&lt;br /&gt;
База динамики : &amp;lt;tex&amp;gt;dp[1] = 0&amp;lt;/tex&amp;gt;, т.к. изначально пила заправлена и высота первого дерева равна 1, по условию задачи.&lt;br /&gt;
Переход динамики :  понятно, что выгодно рубить сначала более дорогие и низкие деревья, а потом более высокие и дешевые (док-во этого факта оставляется читателям как несложное упражнение, т.к. эта идея относится скорее к теме жадных алгоритмнов, чем к теме данной статьи). Поэтому перед  &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-м деревом мы обязательно срубили какое-то &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-е, причем &amp;lt;tex&amp;gt;j \leqslant i - 1&amp;lt;/tex&amp;gt;. Поэтому чтобы найти &amp;lt;tex&amp;gt;dp[i]&amp;lt;/tex&amp;gt; нужно перебрать все &amp;lt;tex&amp;gt;1 \leqslant j \leqslant i - 1&amp;lt;/tex&amp;gt; и попытаться использовать ответ для дерева намер &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;. Итак, пусть перед &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-м деревом мы полностью срубили &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-е, причем высота &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева составляет &amp;lt;tex&amp;gt;a[i]&amp;lt;/tex&amp;gt;, а т.к. последнее дерево, которое мы срубили имеет индекс &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, то стоимость каждого метра &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева составит &amp;lt;tex&amp;gt;c[j]&amp;lt;/tex&amp;gt;.  Поэтому на сруб &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева мы потратим &amp;lt;tex&amp;gt;a[i] \cdot c[j]&amp;lt;/tex&amp;gt; монет. Также не стоит забывать, ситуацию, когда  &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-е дерево полностью срублено, мы получили не бесплатно, а за &amp;lt;tex&amp;gt;dp[j]&amp;lt;/tex&amp;gt; монет.&lt;br /&gt;
Итогвая формула пересчета : &amp;lt;tex&amp;gt;dp[i] = \min\limits_{j=1...i-1} (dp[j] + a[i] \cdot c[j])&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Посмотрим на код выше описанного решения:&lt;br /&gt;
 '''int''' &amp;lt;tex&amp;gt;\mathtt{simpleDP}&amp;lt;/tex&amp;gt;('''int''' a[n], '''int''' c[n])&lt;br /&gt;
    dp[1] = 0&lt;br /&gt;
    dp[2] = dp[3] = ... = dp[n] = &amp;lt;tex&amp;gt;\infty&amp;lt;/tex&amp;gt;&lt;br /&gt;
    '''for'''  i = 1..n-1  &lt;br /&gt;
       dp[i] = &amp;lt;tex&amp;gt;+\infty&amp;lt;/tex&amp;gt;&lt;br /&gt;
       '''for''' j = 0..i-1 &lt;br /&gt;
           '''if''' (dp[j] + a[i] &amp;lt;tex&amp;gt;\cdot&amp;lt;/tex&amp;gt; c[j] &amp;lt; dp[i])&lt;br /&gt;
           dp[i] = dp[j] + a[i] &amp;lt;tex&amp;gt;\cdot&amp;lt;/tex&amp;gt; c[j]&lt;br /&gt;
 '''return''' dp[n]&lt;br /&gt;
Нетрудно видеть, что такая динамика работает за &amp;lt;tex&amp;gt;O(n^2)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Ключевая идея оптимизации==&lt;br /&gt;
Для начала сделаем замену обозначений. Давайте обозначим &amp;lt;tex&amp;gt;dp[j]&amp;lt;/tex&amp;gt; за &amp;lt;tex&amp;gt;b[j]&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;a[i]&amp;lt;/tex&amp;gt; за &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt;, а &amp;lt;tex&amp;gt;c[j]&amp;lt;/tex&amp;gt; за &amp;lt;tex&amp;gt;k[j]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь формула приняла вид &amp;lt;tex&amp;gt;dp[i] = \min\limits_{j=0...i-1}(k[j] \cdot x[i] + b[j])&amp;lt;/tex&amp;gt;. Выражение &amp;lt;tex&amp;gt;k[j] \cdot x + b[j]&amp;lt;/tex&amp;gt; - это в точности уравнение прямой вида &amp;lt;tex&amp;gt;y = kx + b&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сопоставим каждому &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, обработанному ранее, прямую &amp;lt;tex&amp;gt;y[j](x) = k[j] \cdot x + b[j]&amp;lt;/tex&amp;gt;. Из условия «&amp;lt;tex&amp;gt;c[i]&amp;lt;/tex&amp;gt; убывают &amp;lt;tex&amp;gt;\Leftrightarrow  k[j]&amp;lt;/tex&amp;gt; уменьшаются с номером &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;» следует то, что прямые, полученные ранее отсортированы в порядке убывания углового коэффициент. Давайте нарисуем несколько таких прямых :&lt;br /&gt;
&lt;br /&gt;
[[Файл:picture1convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
Выделим множество точек &amp;lt;tex&amp;gt;(x0, y0)&amp;lt;/tex&amp;gt; , таких что все они принадлежат одной из прямых и при этом нету ни одной прямой &amp;lt;tex&amp;gt;y’(x)&amp;lt;/tex&amp;gt;, такой что &amp;lt;tex&amp;gt;y’(x0) &amp;lt; y0&amp;lt;/tex&amp;gt;. Иными словами возьмем «выпуклую (вверх) оболочку» нашего множества прямых (её еще называют нижней ошибающей множества прямых на плоскости). Назовем ее «&amp;lt;tex&amp;gt;y = convex(x)&amp;lt;/tex&amp;gt;». Видно, что множество точек &amp;lt;math&amp;gt;(x, convex(x))&amp;lt;/math&amp;gt; представляет собой выпуклую вверх функцию.&lt;br /&gt;
&lt;br /&gt;
==Цель нижней огибающей множества прямых==&lt;br /&gt;
Пусть мы считаем динамику для &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева. Его задает &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt;. Итак, нам нужно для данного &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt; найти &amp;lt;tex&amp;gt;\min\limits_{j=0..i-1}(k[j] \cdot x[i] + b[i]) = \min\limits_{j=0..i-1}(y[j](x[i]))&amp;lt;/tex&amp;gt;. Это выражение есть &amp;lt;math&amp;gt;convex(x[i])&amp;lt;/math&amp;gt;. Из монотонности угловых коэффицентов отрезков, задающих выпуклую оболочку, и их расположения по координаты x следует то, что отрезок, который пересекает прямую &amp;lt;tex&amp;gt;x = x[i]&amp;lt;/tex&amp;gt;, можно найти бинарным поиском. Это потребует &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; времени на поиск такого &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;dp[i] = k[j] \cdot x[i] + b[j]&amp;lt;/tex&amp;gt;. Теперь осталось научиться поддерживать множество прямых и быстро добавлять &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-ю прямую после того, как мы посчитали &amp;lt;tex&amp;gt;b[i] = dp[i]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Воспользуемся идеей алгоритма построения выпуклой оболочки множества точек. Заведем 2 стека &amp;lt;tex&amp;gt;k[]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;b[]&amp;lt;/tex&amp;gt;, которые задают прямые в отсортированном порядке их угловыми коэффицентами и свободными членами. Рассмотрим ситуацию, когда мы хотим добавить новую (&amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-тую) прямую в множество. Пусть сейчас в множестве лежит &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt; прямых (нумерация с 1). Пусть &amp;lt;tex&amp;gt;(xL, yL)&amp;lt;/tex&amp;gt; - точка пересечения &amp;lt;tex&amp;gt;sz - 1&amp;lt;/tex&amp;gt;-й прямой множества и &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-й, а &amp;lt;tex&amp;gt;(xR, yR)&amp;lt;/tex&amp;gt; - точка пересечения новой прямой, которую мы хотим добавить в конец множества и &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-й. Нас будут интересовать только их &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;-овые координаты &amp;lt;tex&amp;gt;xL&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;xR&amp;lt;/tex&amp;gt;, соответственно. Если оказалось, что новая прямая пересекает &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-ю прямую выпуклой оболочки позже, чем &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-я &amp;lt;tex&amp;gt;sz - 1&amp;lt;/tex&amp;gt;-ю, т.е. &amp;lt;tex&amp;gt;(xL \geqslant xR)&amp;lt;/tex&amp;gt;, то &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-ю удалим из нашего множества, иначе - остановимся. Так будем делать, пока либо кол-во прямых в стеке не станет равным 2, либо &amp;lt;tex&amp;gt;xL&amp;lt;/tex&amp;gt; не станет меньше &amp;lt;tex&amp;gt;xR.&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Асимптотика : аналогично обычному алгоритму построения выпуклой оболочки, каждая прямая ровно &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; раз добавится в стек и максимум &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; раз удалится. Значит время работы перестройки выпуклой оболочки займет &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; суммарно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:picture2convexhull.png]]&lt;br /&gt;
[[Файл:picture3convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
{{Теорема&lt;br /&gt;
|id=th1239. &lt;br /&gt;
|statement=Алгоритм построения нижней огибающей множества прямых корректен.&lt;br /&gt;
|proof=Достаточно показать, что последнюю прямую нужно удалить из множества т.и т.т., когда она наша новая прямая пересекает ее в точке с координатой по оси X, меньшей, чем последняя - предпоследнюю.&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;Y(x) = Kx + B&amp;lt;/tex&amp;gt; - уравнение новой прямой,  &amp;lt;tex&amp;gt;y[i](x) = K[i]x + B[i]&amp;lt;/tex&amp;gt; - уравнения прямых множества. Тогда т.к. &amp;lt;tex&amp;gt;K &amp;lt; K[sz]&amp;lt;/tex&amp;gt;, то при &amp;lt;tex&amp;gt;x \in [- \infty; xR]  : y[sz](x) &amp;lt;= Y(x)&amp;lt;/tex&amp;gt;, а т.к. &amp;lt;tex&amp;gt; K[sz] &amp;lt; K[sz - 1]&amp;lt;/tex&amp;gt;, то при &amp;lt;tex&amp;gt;x \in [xL; + \infty]  : y[sz - 1](x) \geqslant y[sz](x)&amp;lt;/tex&amp;gt;. Если &amp;lt;tex&amp;gt;xL &amp;lt; xR&amp;lt;/tex&amp;gt;, то при &amp;lt;tex&amp;gt;x \in [xL; xR]  : y[sz - 1] \geqslant y[sz](x) и Y(x) \geqslant y[sz](x)&amp;lt;/tex&amp;gt;, т.е. на отрезке &amp;lt;tex&amp;gt;[xL; xR]&amp;lt;/tex&amp;gt; прямая номер sz лежит ниже остальных и её нужно оставить в множестве. Если же &amp;lt;tex&amp;gt;xL &amp;gt; xR&amp;lt;/tex&amp;gt;, то она ниже всех на отрезке &amp;lt;tex&amp;gt;[xL; xR] = \varnothing &amp;lt;/tex&amp;gt;, т.е. её можно удалить из множества&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==Детали реализации:==&lt;br /&gt;
Будем хранить 2 массива : &amp;lt;tex&amp;gt;front[]&amp;lt;/tex&amp;gt; -- &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;-координаты, начиная с которых прямые совпадают с выпуклой оболочкой (т.е. i-я прямая совпадает с выпуклой оболочкой текущего множества прямых при &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;[front[i]; front[i + 1])&amp;lt;/tex&amp;gt; ) и &amp;lt;tex&amp;gt;st[]&amp;lt;/tex&amp;gt; -- номера деревьев, соответствующих прямым (т.е. &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-я прямая множества, где &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;[1; sz]&amp;lt;/tex&amp;gt; соответствует дереву номер &amp;lt;tex&amp;gt;sz[i]&amp;lt;/tex&amp;gt;). Также воспользуемся тем, что &amp;lt;tex&amp;gt;x[i] = a[i]&amp;lt;/tex&amp;gt; возрастают (по условию задачи), а значит мы можем искать первое такое &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;x[i] \geqslant front[j]&amp;lt;/tex&amp;gt; не бинарным поиском, а методом двух указателей за &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; операций суммарно. Также массив front[] можно хранить в целых числах, округляя х-координаты в сторону лежащих правее по оси x до ближайшего целого (*), т.к. на самом деле мы, считая динамику, подставляем в уравнения прямых только целые &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt;, а значит если &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt;-я прямая пересекается с &amp;lt;tex&amp;gt;k+1&amp;lt;/tex&amp;gt;-й в точке &amp;lt;tex&amp;gt;z +&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\alpha&amp;lt;/tex&amp;gt; (&amp;lt;math&amp;gt;z&amp;lt;/math&amp;gt;-целое,  &amp;lt;tex&amp;gt;\alpha&amp;lt;/tex&amp;gt;  &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;[0;1)&amp;lt;/tex&amp;gt;), то мы будем подставлять в их уравнения &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt;  или &amp;lt;tex&amp;gt;z + 1&amp;lt;/tex&amp;gt;. Поэтому можно считать, что новая прямая начинает совпадать с выпуклой оболочкой, начиная с &amp;lt;tex&amp;gt;x = z+1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Реализация==&lt;br /&gt;
  '''int''' &amp;lt;tex&amp;gt;\mathtt{ConvexHullTrick}&amp;lt;/tex&amp;gt;('''int''' a[n], '''int''' c[n])&lt;br /&gt;
     st[1] = 1&lt;br /&gt;
     from[1] = -&amp;lt;tex&amp;gt;\infty&amp;lt;/tex&amp;gt;&amp;lt;font color=green&amp;gt;// первая прямая покрывает все x-ы, начиная с -∞ &amp;lt;/font&amp;gt;&lt;br /&gt;
     sz = 1 &amp;lt;font color=green&amp;gt;// текущий размер выпуклой оболочки &amp;lt;/font&amp;gt;&lt;br /&gt;
     pos = 1 &amp;lt;font color=green&amp;gt;// текущая позиция первого такого j, что x[i] \geqslant front[st[j]] &amp;lt;/font &amp;gt;&lt;br /&gt;
     '''for'''  i = 2..n  &lt;br /&gt;
          '''while''' (front[pos] &amp;lt; x[i]) &amp;lt;font color=green&amp;gt;// метод 1 указателя (ищем первое pos, такое что x[i] покрывается &amp;quot;областью действия&amp;quot; st[pos]-той прямой &amp;lt;/font &amp;gt;&lt;br /&gt;
               pos = pos + 1&lt;br /&gt;
          j = st[pos]&lt;br /&gt;
          dp[i] = K[j]&amp;lt;math&amp;gt;\cdot&amp;lt;/math&amp;gt;a[i] + B[j] &lt;br /&gt;
          '''if''' (i &amp;lt; n)   &amp;lt;font color=green&amp;gt;// если у нас добавляется НЕ последняя прямая, то придется пересчитать выпуклую оболочку &amp;lt;/font &amp;gt;&lt;br /&gt;
                K[i] = c[i]  &amp;lt;font color=green&amp;gt;// наши переобозначения переменных &amp;lt;/font &amp;gt;&lt;br /&gt;
                B[i] = dp[i] &amp;lt;font color=green&amp;gt;// наши переобозначения переменных &amp;lt;/font &amp;gt;&lt;br /&gt;
                x = -&amp;lt;tex&amp;gt;\infty&amp;lt;/tex&amp;gt; &lt;br /&gt;
                '''while''' ''true'' &lt;br /&gt;
                      j = st[sz]&lt;br /&gt;
                      x = divide(B[j] - B[i], K[i] - K[j]) &amp;lt;font color=green&amp;gt;// x-координата пересечения с последней прямой оболочки, округленное в нужную сторону (*) &amp;lt;/font &amp;gt;&lt;br /&gt;
                      '''if''' (x &amp;gt; from[sz]) '''break'''  &amp;lt;font color=green&amp;gt;// перестаем удалять последнюю прямую из множества, если новая прямая пересекает ее позже, чем начинается ее &amp;quot;область действия&amp;quot; &amp;lt;/font &amp;gt;&lt;br /&gt;
                      sz = sz - 1&amp;lt;font color=green&amp;gt;// удаляем последнюю прямую, если она лишняя &amp;lt;/font &amp;gt;&lt;br /&gt;
                 st[sz + 1] = i  &lt;br /&gt;
                 from[sz + 1] = x &amp;lt;font color=green&amp;gt;// добавили новую прямую &amp;lt;/font &amp;gt;&lt;br /&gt;
                 sz = sz + 1&lt;br /&gt;
  '''return''' dp[n] &lt;br /&gt;
Здесь функция &amp;lt;tex&amp;gt;\mathtt{divide}&amp;lt;/tex&amp;gt;(a, b) возвращает нужное(*) округление a / b. Приведем её код :&lt;br /&gt;
 '''int''' &amp;lt;tex&amp;gt;\mathtt{divide}&amp;lt;/tex&amp;gt;('''int''' a, '''int''' b)&lt;br /&gt;
        delta = 0&lt;br /&gt;
        '''if''' (a '''mod''' b ≠ 0) delta = 1&lt;br /&gt;
        '''if''' ((a &amp;gt; 0 '''and''' b &amp;gt; 0) '''or''' (a &amp;lt; 0 '''and''' b &amp;lt; 0)) '''return''' [a / b] + delta&lt;br /&gt;
 '''return''' -[|a| / |b|]&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
Такая реализация будет работать за O(n).&lt;br /&gt;
&lt;br /&gt;
==Динамический convex hull trick==&lt;br /&gt;
	Заметим, что условия на прямые, что &amp;lt;tex&amp;gt;k[i]&amp;lt;/tex&amp;gt; возрастает/убывает и &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt; убывает/возрастает выглядят достаточно редкими для большинства задач. Пусть в задаче таких ограничений нет. Первый способ борьбы с этой проблемой - отсортировать входные данные нужным образом, не испортив свойств задачи (пример : задача G c Санкт-Петербургских сборов к РОИ 2016[http://neerc.ifmo.ru/school/camp-2016/problems/20160318a.pdf]).&lt;br /&gt;
&lt;br /&gt;
	Но рассмотрим общий случай. По-прежнему у нас есть выпуклая оболочка прямых, имея которую мы за &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; можем найти   &amp;lt;tex&amp;gt;dp[i]&amp;lt;/tex&amp;gt;, но теперь вставку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-й прямой в оболочку уже нельзя выполнить описанным ранее способом за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt; в среднем. У нас есть выпуклая оболочка, наша прямая пересекает ее, возможно, «отсекая» несколько отрезков выпуклой оболочки в середине (рис. 4 : красная прямая - та, которую мы хотим вставить в наше множество). Более формально : теперь наша новая прямая будет ниже остальных при &amp;lt;tex&amp;gt;x \in [x1; x2]&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;x1, x2 \in R&amp;lt;/tex&amp;gt; - точки пересечения с некоторыми прямыми, причем &amp;lt;tex&amp;gt;x2&amp;lt;/tex&amp;gt; не обязательно равно &amp;lt;tex&amp;gt;+ \infty&amp;lt;/tex&amp;gt;&lt;br /&gt;
[[Файл:picture4convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
Чтобы уметь вставлять прямую в множество будем хранить &amp;lt;math&amp;gt;std::set&amp;lt;/math&amp;gt; (или любой аналог в других языках программирования) пар &amp;lt;tex&amp;gt;(k, st)&amp;lt;/tex&amp;gt; =  &amp;lt;tex&amp;gt;(коэффицент прямой, ее номер в глобальной нумерации)&amp;lt;/tex&amp;gt;. Когда приходит новая прямая, ищем последнюю прямую с меньшим угловым коэффицентом, чем у той прямой, которую мы хотим добавить в множество. Поиск такой прямой занимает &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt;. Начиная с найденной прямой выполняем &amp;quot;старый&amp;quot; алгоритм (удаляем, пока текущая прямая множества бесполезна). И симметричный алгоритм применяем ко всем прямым справа от нашей (удаляем правого соседа нашей прямой, пока она пересекает нас позже, чем своего правого соседа).&lt;br /&gt;
	&lt;br /&gt;
Асимптотика решения составит &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; на каждый из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; запросов «добавить прямую» + &amp;lt;tex&amp;gt;O(n\cdot\log(n))&amp;lt;/tex&amp;gt; суммарно на удаление прямых, т.к. по-прежнему каждая прямая не более одного раза удалится из множества, а каждое удаление из std::set занимает &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; времени. Итого &amp;lt;math&amp;gt;O(n\cdot\log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Альтернативный подход ==&lt;br /&gt;
Другой способ интерпретировать выражение &amp;lt;tex&amp;gt;dp[i] = \min\limits_{j=0...i-1}(c[j] \cdot a[i] + dp[j])&amp;lt;/tex&amp;gt;  заключается в том, что мы будем пытаться свести задачу к стандартной выпуклой оболочке множества точек. Перепишем выражение средующим образом : &amp;lt;tex&amp;gt;dp[j] + a[i] \cdot c[j] = (dp[j], c[j]) \cdot (1, a[i])&amp;lt;/tex&amp;gt;, т.е. запишем как скалярное произведение векторов &amp;lt;tex&amp;gt;v[j] = (dp[j], c[j])&amp;lt;/tex&amp;gt; и &amp;lt;tex &amp;gt;u[i] = (1, a[i])&amp;lt;/tex &amp;gt;. Вектора  &amp;lt;tex &amp;gt;v[j] =  (dp[j], c[j])&amp;lt;/tex&amp;gt; хотелось бы организовать так, чтобы за &amp;lt;tex &amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; находить вектор, максимизирующий выражение &amp;lt;tex&amp;gt;v[j] \cdot u[i]&amp;lt;/tex&amp;gt;. Посмотрим на рис. 5. Заметим интуитивно очевидный факт : красная точка (вектор) &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; не может давать более оптимальное значение &amp;lt;tex&amp;gt;v[j] \cdot u[i]&amp;lt;/tex&amp;gt; одновременно чем обе синие точки. По этой причине нам достаточно оставить выпуклую оболочку векторов &amp;lt;tex&amp;gt;v[j]&amp;lt;/tex&amp;gt;, а ответ на запрос - это поиск &amp;lt;tex&amp;gt;v[j]&amp;lt;/tex&amp;gt;, максимизирующего проекцию на &amp;lt;tex&amp;gt;u[i]&amp;lt;/tex&amp;gt;. Это задача поиска ближайшей точки выпуклого многоугольника (составленного из точек выпуклой оболочки) к заданной прямой (из &amp;lt;tex&amp;gt;(0, 0)&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;(1, a[i])&amp;lt;/tex&amp;gt;).  Ее можно решить за &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; двумя бинарными или одним тернарным поиском&lt;br /&gt;
Асимптотика алгоритма по-прежнему составит &amp;lt;tex&amp;gt;O(n \cdot \log(n))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Файл:picture5convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
Докажем то, что описанный выше алгоритм корректен. Для этого достаточно показать, что если имеются &amp;lt;math&amp;gt;3&amp;lt;/math&amp;gt; вектора &amp;lt;math&amp;gt;a, b, c&amp;lt;/math&amp;gt;, расположенные как на рис. 5, т.е. точка &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; не лежит на выпуклой оболочке векторов &amp;lt;tex&amp;gt;0, a, b, c &amp;lt;/tex&amp;gt; : &amp;lt;tex&amp;gt; \Leftrightarrow |[a-b, b-c]| &amp;lt; 0 &amp;lt;/tex&amp;gt;, то либо  &amp;lt;tex&amp;gt;(a, u[i])&amp;lt;/tex&amp;gt; оптимальнее, чем &amp;lt;tex&amp;gt;(b, u[i])&amp;lt;/tex&amp;gt;,  либо &amp;lt;tex&amp;gt;(c, u[i])&amp;lt;/tex&amp;gt; оптимальнее, чем &amp;lt;tex&amp;gt;(b, u[i])&amp;lt;/tex&amp;gt;.&lt;br /&gt;
{{Теорема&lt;br /&gt;
|id=th12392. &lt;br /&gt;
|statement=Если есть &amp;lt;tex&amp;gt;3&amp;lt;/tex&amp;gt; вектора &amp;lt;tex&amp;gt;a, b, c&amp;lt;/tex&amp;gt;, таких что &amp;lt;tex&amp;gt;|[a-b, b-c]| &amp;lt; 0&amp;lt;/tex&amp;gt; то либо &amp;lt;math&amp;gt;(a, u) &amp;lt; (b, u)&amp;lt;/math&amp;gt;, либо &amp;lt;math&amp;gt;(c, u) &amp;lt; (b, u)&amp;lt;/math&amp;gt;, где вектор &amp;lt;math&amp;gt;u = (1; k)&amp;lt;/math&amp;gt;.&lt;br /&gt;
|proof=По условию теоремы известно, что &amp;lt;tex&amp;gt;|[a-b, b-c]| &amp;lt; 0 \Leftrightarrow (a_{x} - b_{x})\cdot(b_{y} - c_{y}) &amp;lt; (a_{y} - b_{y}) \cdot (b_{x} - c_{x})&amp;lt;/tex&amp;gt; (*). Предположим (от противного), что &amp;lt;tex&amp;gt;(b, u) &amp;lt; (a, u) \Leftrightarrow b_{x}  + k \cdot b_{y} &amp;lt; c_{x} + k \cdot c_{y} \Leftrightarrow (b_{x} - c_{x}) &amp;lt; k \cdot (c_{y} - b_{y})&amp;lt;/tex&amp;gt; и  &amp;lt;tex&amp;gt;(b, u) &amp;lt; (c, u) \Leftrightarrow b_{x}  + k \cdot b_{y} &amp;lt; a_{x} + k \cdot a_{y} \Leftrightarrow (a_{x} - b_{x}) &amp;gt; k \cdot (b_{y} - a_{y})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Подставим эти неравенства в (*). Получим цепочку неравенств : &amp;lt;tex&amp;gt;k \cdot (a_{y} - b_{y})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (c_{y} - b_{y}) = k&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (b_{y} - a_{y}) \cdot &amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt;(b_{y} - c_{y})&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt; &amp;lt; (a_{x} - b_{x})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (b_{y} - c_{y})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; &amp;lt; (a_{y} - b_{y}) \cdot &amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt;(b_{x} - c_{x})&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;&amp;lt; k \cdot (a_{y} - b_{y})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (c_{y} - b_{y})&amp;lt;/tex&amp;gt;. Получили противоречие : &amp;lt;tex&amp;gt;k \cdot (a_{y} - b_{y}) \cdot (c_{y} - b_{y}) &amp;lt; k \cdot (a_{y} - b_{y}) \cdot (c_{y} - b_{y})&amp;lt;/tex&amp;gt;. Значит предположение неверно, чтд.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Из доказанной теоремы и следует корректность алгоритма.&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
1) http://neerc.ifmo.ru/wiki/index.php?title=Статические_выпуклые_оболочки:_Джарвис,_Грэхем,_Эндрю,_Чен,_QuickHull&lt;br /&gt;
&lt;br /&gt;
2) http://neerc.ifmo.ru/wiki/index.php?title=Динамическое_программирование&lt;br /&gt;
&lt;br /&gt;
[[Категория:Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория: Динамическое программирование]]&lt;br /&gt;
[[Категория: Способы оптимизации методов динамического программирования]]&lt;/div&gt;</summary>
		<author><name>92.255.113.52</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=Convex_hull_trick&amp;diff=60032</id>
		<title>Convex hull trick</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=Convex_hull_trick&amp;diff=60032"/>
				<updated>2017-01-18T17:23:07Z</updated>
		
		<summary type="html">&lt;p&gt;92.255.113.52: /* Детали реализации: */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Convex hull trick -- один из методов оптимизации динамического программирования [[http://neerc.ifmo.ru/wiki/index.php?title=Динамическое_программирование]], использующий идею выпуклой оболочки. Позволяет улучшить ассимптотику решения некоторых задачь, решемых методом динамического программирования с &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt; до &amp;lt;tex&amp;gt;O(n\cdot\log(n))&amp;lt;/tex&amp;gt;. Техника впервые появилась в 1995 году (задачу на нее предложили в USACO -- национальной олимпиаде США по программированию). Массовую известность получила после IOI (международной олимпиады по программированию для школьников) 2002.&lt;br /&gt;
&lt;br /&gt;
==Пример задачи, решаемой методом convex hull trick==&lt;br /&gt;
Рассмотрим задачу на ДП:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; деревьев с высотами &amp;lt;tex&amp;gt;a_1, a_2, \dots, a_n&amp;lt;/tex&amp;gt; (в метрах). Требуется спилить их все, потратив минимальное количество монет на заправку&lt;br /&gt;
бензопилы. Но пила устроена так, что она может спиливать только по 1 метру от дерева, к которому ее применили. Также после&lt;br /&gt;
срубленного метра (любого дерева) пилу нужно заправлять, платя за  бензин определенной кол-во монет. Причем стоимость &lt;br /&gt;
бензина зависит от срубленных (полностью) деревьев. Если сейчас максимальный индекс срубленного дерева равен &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, то цена заправки &lt;br /&gt;
равна &amp;lt;tex&amp;gt;c_i&amp;lt;/tex&amp;gt;.  Изначально пила заправлена.&lt;br /&gt;
Также известны следующие ограничения : &amp;lt;tex&amp;gt;c_n = 0, a_1 = 1, a_i&amp;lt;/tex&amp;gt; возрастают, &amp;lt;tex&amp;gt;c_i&amp;lt;/tex&amp;gt; убывают. Изначально пила заправлена.&lt;br /&gt;
(убывание и возрастание нестрогие)&lt;br /&gt;
}}&lt;br /&gt;
(Задача H с Санкт-Петербургских сборов к РОИ 2016[http://neerc.ifmo.ru/school/camp-2016/problems/20160318a.pdf])&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
&amp;lt;includeonly&amp;gt;{{#if: {{{neat|}}}|&lt;br /&gt;
&amp;lt;div style=&amp;quot;background-color: #fcfcfc; float:left;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;background-color: #ddd;&amp;quot;&amp;gt;'''Задача:'''&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;border:1px dashed #2f6fab; padding: 8px; font-style: italic;&amp;quot;&amp;gt;{{{definition}}}&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;|&lt;br /&gt;
&amp;lt;table border=&amp;quot;0&amp;quot; width=&amp;quot;100%&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td style=&amp;quot;background-color: #ddd&amp;quot;&amp;gt;'''Задача:'''&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td style=&amp;quot;border:1px dashed #2f6fab; padding: 8px; background-color: #fcfcfc; font-style: italic;&amp;quot;&amp;gt;{{{definition}}}&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;}}&lt;br /&gt;
&amp;lt;/includeonly&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Наивное решение==&lt;br /&gt;
Сначала заметим важный факт : т.к. &amp;lt;tex&amp;gt;c[i]&amp;lt;/tex&amp;gt; убывают (нестрого) и &amp;lt;tex&amp;gt;c[n] = 0&amp;lt;/tex&amp;gt;, то все &amp;lt;tex&amp;gt;c[i]&amp;lt;/tex&amp;gt; неотрицательны.&lt;br /&gt;
Понятно, что нужно затратив минимальную стоимость срубить последнее (&amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;-е) дерево, т.к. после него все деревья можно будет рубить бесплатно (т.к. &amp;lt;tex&amp;gt;c[n] = 0&amp;lt;/tex&amp;gt;). Посчитаем следующую динамику : &amp;lt;tex&amp;gt;dp[i]&amp;lt;/tex&amp;gt; - минимальная стоимость, заплатив которую можно добиться того, что дерево номер &amp;lt;tex&amp;gt;i.&amp;lt;/tex&amp;gt; будет срублено.&lt;br /&gt;
База динамики : &amp;lt;tex&amp;gt;dp[1] = 0&amp;lt;/tex&amp;gt;, т.к. изначально пила заправлена и высота первого дерева равна 1, по условию задачи.&lt;br /&gt;
Переход динамики :  понятно, что выгодно рубить сначала более дорогие и низкие деревья, а потом более высокие и дешевые (док-во этого факта оставляется читателям как несложное упражнение, т.к. эта идея относится скорее к теме жадных алгоритмнов, чем к теме данной статьи). Поэтому перед  &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-м деревом мы обязательно срубили какое-то &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-е, причем &amp;lt;tex&amp;gt;j \leqslant i - 1&amp;lt;/tex&amp;gt;. Поэтому чтобы найти &amp;lt;tex&amp;gt;dp[i]&amp;lt;/tex&amp;gt; нужно перебрать все &amp;lt;tex&amp;gt;1 \leqslant j \leqslant i - 1&amp;lt;/tex&amp;gt; и попытаться использовать ответ для дерева намер &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;. Итак, пусть перед &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-м деревом мы полностью срубили &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-е, причем высота &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева составляет &amp;lt;tex&amp;gt;a[i]&amp;lt;/tex&amp;gt;, а т.к. последнее дерево, которое мы срубили имеет индекс &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, то стоимость каждого метра &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева составит &amp;lt;tex&amp;gt;c[j]&amp;lt;/tex&amp;gt;.  Поэтому на сруб &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева мы потратим &amp;lt;tex&amp;gt;a[i] \cdot c[j]&amp;lt;/tex&amp;gt; монет. Также не стоит забывать, ситуацию, когда  &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-е дерево полностью срублено, мы получили не бесплатно, а за &amp;lt;tex&amp;gt;dp[j]&amp;lt;/tex&amp;gt; монет.&lt;br /&gt;
Итогвая формула пересчета : &amp;lt;tex&amp;gt;dp[i] = \min\limits_{j=1...i-1} (dp[j] + a[i] \cdot c[j])&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Посмотрим на код выше описанного решения:&lt;br /&gt;
 '''int''' &amp;lt;tex&amp;gt;\mathtt{simpleDP}&amp;lt;/tex&amp;gt;('''int''' a[n], '''int''' c[n])&lt;br /&gt;
    dp[1] = 0&lt;br /&gt;
    dp[2] = dp[3] = ... = dp[n] = &amp;lt;tex&amp;gt;\infty&amp;lt;/tex&amp;gt;&lt;br /&gt;
    '''for'''  i = 1..n-1  &lt;br /&gt;
       dp[i] = &amp;lt;tex&amp;gt;+\infty&amp;lt;/tex&amp;gt;&lt;br /&gt;
       '''for''' j = 0..i-1 &lt;br /&gt;
           '''if''' (dp[j] + a[i] &amp;lt;tex&amp;gt;\cdot&amp;lt;/tex&amp;gt; c[j] &amp;lt; dp[i])&lt;br /&gt;
           dp[i] = dp[j] + a[i] &amp;lt;tex&amp;gt;\cdot&amp;lt;/tex&amp;gt; c[j]&lt;br /&gt;
 '''return''' dp[n]&lt;br /&gt;
Нетрудно видеть, что такая динамика работает за &amp;lt;tex&amp;gt;O(n^2)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Ключевая идея оптимизации==&lt;br /&gt;
Для начала сделаем замену обозначений. Давайте обозначим &amp;lt;tex&amp;gt;dp[j]&amp;lt;/tex&amp;gt; за &amp;lt;tex&amp;gt;b[j]&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;a[i]&amp;lt;/tex&amp;gt; за &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt;, а &amp;lt;tex&amp;gt;c[j]&amp;lt;/tex&amp;gt; за &amp;lt;tex&amp;gt;k[j]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь формула приняла вид &amp;lt;tex&amp;gt;dp[i] = \min\limits_{j=0...i-1}(k[j] \cdot x[i] + b[j])&amp;lt;/tex&amp;gt;. Выражение &amp;lt;tex&amp;gt;k[j] \cdot x + b[j]&amp;lt;/tex&amp;gt; - это в точности уравнение прямой вида &amp;lt;tex&amp;gt;y = kx + b&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сопоставим каждому &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, обработанному ранее, прямую &amp;lt;tex&amp;gt;y[j](x) = k[j] \cdot x + b[j]&amp;lt;/tex&amp;gt;. Из условия «&amp;lt;tex&amp;gt;c[i]&amp;lt;/tex&amp;gt; убывают &amp;lt;tex&amp;gt;\Leftrightarrow  k[j]&amp;lt;/tex&amp;gt; уменьшаются с номером &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;» следует то, что прямые, полученные ранее отсортированы в порядке убывания углового коэффициент. Давайте нарисуем несколько таких прямых :&lt;br /&gt;
&lt;br /&gt;
[[Файл:picture1convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
Выделим множество точек &amp;lt;tex&amp;gt;(x0, y0)&amp;lt;/tex&amp;gt; , таких что все они принадлежат одной из прямых и при этом нету ни одной прямой &amp;lt;tex&amp;gt;y’(x)&amp;lt;/tex&amp;gt;, такой что &amp;lt;tex&amp;gt;y’(x0) &amp;lt; y0&amp;lt;/tex&amp;gt;. Иными словами возьмем «выпуклую (вверх) оболочку» нашего множества прямых (её еще называют нижней ошибающей множества прямых на плоскости). Назовем ее «&amp;lt;tex&amp;gt;y = convex(x)&amp;lt;/tex&amp;gt;». Видно, что множество точек &amp;lt;math&amp;gt;(x, convex(x))&amp;lt;/math&amp;gt; представляет собой выпуклую вверх функцию.&lt;br /&gt;
&lt;br /&gt;
==Цель нижней огибающей множества прямых==&lt;br /&gt;
Пусть мы считаем динамику для &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева. Его задает &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt;. Итак, нам нужно для данного &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt; найти &amp;lt;tex&amp;gt;\min\limits_{j=0..i-1}(k[j] \cdot x[i] + b[i]) = \min\limits_{j=0..i-1}(y[j](x[i]))&amp;lt;/tex&amp;gt;. Это выражение есть &amp;lt;math&amp;gt;convex(x[i])&amp;lt;/math&amp;gt;. Из монотонности угловых коэффицентов отрезков, задающих выпуклую оболочку, и их расположения по координаты x следует то, что отрезок, который пересекает прямую &amp;lt;tex&amp;gt;x = x[i]&amp;lt;/tex&amp;gt;, можно найти бинарным поиском. Это потребует &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; времени на поиск такого &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;dp[i] = k[j] \cdot x[i] + b[j]&amp;lt;/tex&amp;gt;. Теперь осталось научиться поддерживать множество прямых и быстро добавлять &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-ю прямую после того, как мы посчитали &amp;lt;tex&amp;gt;b[i] = dp[i]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Воспользуемся идеей алгоритма построения выпуклой оболочки множества точек. Заведем 2 стека &amp;lt;tex&amp;gt;k[]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;b[]&amp;lt;/tex&amp;gt;, которые задают прямые в отсортированном порядке их угловыми коэффицентами и свободными членами. Рассмотрим ситуацию, когда мы хотим добавить новую (&amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-тую) прямую в множество. Пусть сейчас в множестве лежит &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt; прямых (нумерация с 1). Пусть &amp;lt;tex&amp;gt;(xL, yL)&amp;lt;/tex&amp;gt; - точка пересечения &amp;lt;tex&amp;gt;sz - 1&amp;lt;/tex&amp;gt;-й прямой множества и &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-й, а &amp;lt;tex&amp;gt;(xR, yR)&amp;lt;/tex&amp;gt; - точка пересечения новой прямой, которую мы хотим добавить в конец множества и &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-й. Нас будут интересовать только их &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;-овые координаты &amp;lt;tex&amp;gt;xL&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;xR&amp;lt;/tex&amp;gt;, соответственно. Если оказалось, что новая прямая пересекает &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-ю прямую выпуклой оболочки позже, чем &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-я &amp;lt;tex&amp;gt;sz - 1&amp;lt;/tex&amp;gt;-ю, т.е. &amp;lt;tex&amp;gt;(xL \geqslant xR)&amp;lt;/tex&amp;gt;, то &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-ю удалим из нашего множества, иначе - остановимся. Так будем делать, пока либо кол-во прямых в стеке не станет равным 2, либо &amp;lt;tex&amp;gt;xL&amp;lt;/tex&amp;gt; не станет меньше &amp;lt;tex&amp;gt;xR.&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Асимптотика : аналогично обычному алгоритму построения выпуклой оболочки, каждая прямая ровно &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; раз добавится в стек и максимум &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; раз удалится. Значит время работы перестройки выпуклой оболочки займет &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; суммарно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:picture2convexhull.png]]&lt;br /&gt;
[[Файл:picture3convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
{{Теорема&lt;br /&gt;
|id=th1239. &lt;br /&gt;
|statement=Алгоритм построения нижней огибающей множества прямых корректен.&lt;br /&gt;
|proof=Достаточно показать, что последнюю прямую нужно удалить из множества т.и т.т., когда она наша новая прямая пересекает ее в точке с координатой по оси X, меньшей, чем последняя - предпоследнюю.&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;Y(x) = Kx + B&amp;lt;/tex&amp;gt; - уравнение новой прямой,  &amp;lt;tex&amp;gt;y[i](x) = K[i]x + B[i]&amp;lt;/tex&amp;gt; - уравнения прямых множества. Тогда т.к. &amp;lt;tex&amp;gt;K &amp;lt; K[sz]&amp;lt;/tex&amp;gt;, то при &amp;lt;tex&amp;gt;x \in [- \infty; xR]  : y[sz](x) &amp;lt;= Y(x)&amp;lt;/tex&amp;gt;, а т.к. &amp;lt;tex&amp;gt; K[sz] &amp;lt; K[sz - 1]&amp;lt;/tex&amp;gt;, то при &amp;lt;tex&amp;gt;x \in [xL; + \infty]  : y[sz - 1](x) \geqslant y[sz](x)&amp;lt;/tex&amp;gt;. Если &amp;lt;tex&amp;gt;xL &amp;lt; xR&amp;lt;/tex&amp;gt;, то при &amp;lt;tex&amp;gt;x \in [xL; xR]  : y[sz - 1] \geqslant y[sz](x) и Y(x) \geqslant y[sz](x)&amp;lt;/tex&amp;gt;, т.е. на отрезке &amp;lt;tex&amp;gt;[xL; xR]&amp;lt;/tex&amp;gt; прямая номер sz лежит ниже остальных и её нужно оставить в множестве. Если же &amp;lt;tex&amp;gt;xL &amp;gt; xR&amp;lt;/tex&amp;gt;, то она ниже всех на отрезке &amp;lt;tex&amp;gt;[xL; xR] = \varnothing &amp;lt;/tex&amp;gt;, т.е. её можно удалить из множества&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==Детали реализации:==&lt;br /&gt;
Будем хранить 2 массива : &amp;lt;tex&amp;gt;front[]&amp;lt;/tex&amp;gt; -- &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;-координаты, начиная с которых прямые совпадают с выпуклой оболочкой (т.е. i-я прямая совпадает с выпуклой оболочкой текущего множества прямых при &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;[front[i]; front[i + 1])&amp;lt;/tex&amp;gt; ) и &amp;lt;tex&amp;gt;st[]&amp;lt;/tex&amp;gt; -- номера деревьев, соответствующих прямым (т.е. &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-я прямая множества, где &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;[1; sz]&amp;lt;/tex&amp;gt; соответствует дереву номер &amp;lt;tex&amp;gt;sz[i]&amp;lt;/tex&amp;gt;). Также воспользуемся тем, что &amp;lt;tex&amp;gt;x[i] = a[i]&amp;lt;/tex&amp;gt; возрастают (по условию задачи), а значит мы можем искать первое такое &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;x[i] \geqslant front[j]&amp;lt;/tex&amp;gt; не бинарным поиском, а методом двух указателей за &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; операций суммарно. Также массив front[] можно хранить в целых числах, округляя х-координаты в сторону лежащих правее по оси x до ближайшего целого (*), т.к. на самом деле мы, считая динамику, подставляем в уравнения прямых только целые &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt;, а значит если &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt;-я прямая пересекается с &amp;lt;tex&amp;gt;k+1&amp;lt;/tex&amp;gt;-й в точке &amp;lt;tex&amp;gt;z +&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\alpha&amp;lt;/tex&amp;gt; (&amp;lt;math&amp;gt;z&amp;lt;/math&amp;gt;-целое,  &amp;lt;tex&amp;gt;\alpha&amp;lt;/tex&amp;gt;  &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;[0;1)&amp;lt;/tex&amp;gt;), то мы будем подставлять в их уравнения &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt;  или &amp;lt;tex&amp;gt;z + 1&amp;lt;/tex&amp;gt;. Поэтому можно считать, что новая прямая начинает совпадать с выпуклой оболочкой, начиная с &amp;lt;tex&amp;gt;x = z+1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Реализация==&lt;br /&gt;
  '''int''' &amp;lt;tex&amp;gt;\mathtt{ConvexHullTrick}&amp;lt;/tex&amp;gt;('''int''' a[n], '''int''' c[n])&lt;br /&gt;
     st[1] = 1&lt;br /&gt;
     from[1] = -&amp;lt;tex&amp;gt;\infty&amp;lt;/tex&amp;gt;&amp;lt;font color=green&amp;gt;// первая прямая покрывает все x-ы, начиная с -∞ &amp;lt;/font&amp;gt;&lt;br /&gt;
     sz = 1 &amp;lt;font color=green&amp;gt;// текущий размер выпуклой оболочки &amp;lt;/font&amp;gt;&lt;br /&gt;
     pos = 1 &amp;lt;font color=green&amp;gt;// текущая позиция первого такого j, что x[i] \geqslant front[st[j]] &amp;lt;/font &amp;gt;&lt;br /&gt;
     '''for'''  i = 2..n  &lt;br /&gt;
          '''while''' (front[pos] &amp;lt; x[i]) &amp;lt;font color=green&amp;gt;// метод 1 указателя (ищем первое pos, такое что x[i] покрывается &amp;quot;областью действия&amp;quot; st[pos]-той прямой &amp;lt;/font &amp;gt;&lt;br /&gt;
               pos = pos + 1&lt;br /&gt;
          j = st[pos]&lt;br /&gt;
          dp[i] = K[j]&amp;lt;math&amp;gt;\cdot&amp;lt;/math&amp;gt;a[i] + B[j] &lt;br /&gt;
          '''if''' (i &amp;lt; n)   &amp;lt;font color=green&amp;gt;// если у нас добавляется НЕ последняя прямая, то придется пересчитать выпуклую оболочку &amp;lt;/font &amp;gt;&lt;br /&gt;
                K[i] = c[i]  &amp;lt;font color=green&amp;gt;// наши переобозначения переменных &amp;lt;/font &amp;gt;&lt;br /&gt;
                B[i] = dp[i] &amp;lt;font color=green&amp;gt;// наши переобозначения переменных &amp;lt;/font &amp;gt;&lt;br /&gt;
                x = -&amp;lt;tex&amp;gt;\infty&amp;lt;/tex&amp;gt; &lt;br /&gt;
                '''while''' ''true'' &lt;br /&gt;
                      j = st[sz]&lt;br /&gt;
                      x = divide(B[j] - B[i], K[i] - K[j]) &amp;lt;font color=green&amp;gt;// x-координата пересечения с последней прямой оболочки, округленное в нужную сторону (*) &amp;lt;/font &amp;gt;&lt;br /&gt;
                      '''if''' (x &amp;gt; from[sz]) '''break'''  &amp;lt;font color=green&amp;gt;// перестаем удалять последнюю прямую из множества, если новая прямая пересекает ее позже, чем начинается ее &amp;quot;область действия&amp;quot; &amp;lt;/font &amp;gt;&lt;br /&gt;
                      sz = sz - 1&amp;lt;font color=green&amp;gt;// удаляем последнюю прямую, если она лишняя &amp;lt;/font &amp;gt;&lt;br /&gt;
                 st[sz + 1] = i  &lt;br /&gt;
                 from[sz + 1] = x &amp;lt;font color=green&amp;gt;// добавили новую прямую &amp;lt;/font &amp;gt;&lt;br /&gt;
                 sz = sz + 1&lt;br /&gt;
  '''return''' dp[n] &lt;br /&gt;
Здесь функция &amp;lt;tex&amp;gt;\mathtt{divide}&amp;lt;/tex&amp;gt;(a, b) возвращает нужное(*) округление a / b. Приведем её код :&lt;br /&gt;
 '''int''' &amp;lt;tex&amp;gt;\mathtt{divide}&amp;lt;/tex&amp;gt;('''int''' a, '''int''' b)&lt;br /&gt;
        delta = 0&lt;br /&gt;
        '''if''' (a '''mod''' b ≠ 0) delta = 1&lt;br /&gt;
        '''if''' ((a &amp;gt; 0 '''and''' b &amp;gt; 0) '''or''' (a &amp;lt; 0 '''and''' b &amp;lt; 0)) '''return''' [a / b] + delta&lt;br /&gt;
 '''return''' -[|a| / |b|]&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
Такая реализация будет работать за O(n).&lt;br /&gt;
&lt;br /&gt;
==Динамический convex hull trick==&lt;br /&gt;
	Заметим, что условия на прямые, что &amp;lt;tex&amp;gt;k[i]&amp;lt;/tex&amp;gt; возрастает/убывает и &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt; убывает/возрастает выглядят достаточно редкими для большинства задач. Пусть в задаче таких ограничений нет. Первый способ борьбы с этой проблемой - отсортировать входные данные нужным образом, не испортив свойств задачи (пример : задача G c Санкт-Петербургских сборов к РОИ 2016[http://neerc.ifmo.ru/school/camp-2016/problems/20160318a.pdf]).&lt;br /&gt;
&lt;br /&gt;
	Но рассмотрим общий случай. По-прежнему у нас есть выпуклая оболочка прямых, имея которую мы за &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; можем найти   &amp;lt;tex&amp;gt;dp[i]&amp;lt;/tex&amp;gt;, но теперь вставку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-й прямой в оболочку уже нельзя выполнить описанным ранее способом за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt; в среднем. У нас есть выпуклая оболочка, наша прямая пересекает ее, возможно, «отсекая» несколько отрезков выпуклой оболочки в середине (рис. 4 : красная прямая - та, которую мы хотим вставить в наше множество). Более формально : теперь наша новая прямая будет ниже остальных при &amp;lt;tex&amp;gt;x \in [x1; x2]&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;x1, x2 \in R&amp;lt;/tex&amp;gt; - точки пересечения с некоторыми прямыми, причем &amp;lt;tex&amp;gt;x2&amp;lt;/tex&amp;gt; не обязательно равно &amp;lt;tex&amp;gt;+ \infty&amp;lt;/tex&amp;gt;&lt;br /&gt;
[[Файл:picture4convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
Чтобы уметь вставлять прямую в множество будем хранить &amp;lt;math&amp;gt;std::set&amp;lt;/math&amp;gt; (или любой аналог в других языках программирования) пар &amp;lt;tex&amp;gt;(k, st)&amp;lt;/tex&amp;gt; =  &amp;lt;tex&amp;gt;(коэффицент прямой, ее номер в глобальной нумерации)&amp;lt;/tex&amp;gt;. Когда приходит новая прямая, ищем последнюю прямую с меньшим угловым коэффицентом, чем у той прямой, которую мы хотим добавить в множество. Поиск такой прямой занимает &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt;. Начиная с найденной прямой выполняем &amp;quot;старый&amp;quot; алгоритм (удаляем, пока текущая прямая множества бесполезна). И симметричный алгоритм применяем ко всем прямым справа от нашей (удаляем правого соседа нашей прямой, пока она пересекает нас позже, чем своего правого соседа).&lt;br /&gt;
	&lt;br /&gt;
Асимптотика решения составит &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; на каждый из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; запросов «добавить прямую» + &amp;lt;tex&amp;gt;O(n\cdot\log(n))&amp;lt;/tex&amp;gt; суммарно на удаление прямых, т.к. по-прежнему каждая прямая не более одного раза удалится из множества, а каждое удаление из std::set занимает &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; времени. Итого &amp;lt;math&amp;gt;O(n\cdot\log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Альтернативный подход ==&lt;br /&gt;
Другой способ интерпретировать выражение &amp;lt;tex&amp;gt;dp[i] = \min\limits_{j=0...i-1}(c[j] \cdot a[i] + dp[j])&amp;lt;/tex&amp;gt;  заключается в том, что мы будем пытаться свести задачу к стандартной выпуклой оболочке множества точек. Перепишем выражение средующим образом : &amp;lt;tex&amp;gt;dp[j] + a[i] \cdot c[j] = (dp[j], c[j]) \cdot (1, a[i])&amp;lt;/tex&amp;gt;, т.е. запишем как скалярное произведение векторов &amp;lt;tex&amp;gt;v[j] = (dp[j], c[j])&amp;lt;/tex&amp;gt; и &amp;lt;tex &amp;gt;u[i] = (1, a[i])&amp;lt;/tex &amp;gt;. Вектора  &amp;lt;tex &amp;gt;v[j] =  (dp[j], c[j])&amp;lt;/tex&amp;gt; хотелось бы организовать так, чтобы за &amp;lt;tex &amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; находить вектор, максимизирующий выражение &amp;lt;tex&amp;gt;v[j] \cdot u[i]&amp;lt;/tex&amp;gt;. Посмотрим на рис. 5. Заметим интуитивно очевидный факт : красная точка (вектор) &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; не может давать более оптимальное значение &amp;lt;tex&amp;gt;v[j] \cdot u[i]&amp;lt;/tex&amp;gt; одновременно чем обе синие точки. По этой причине нам достаточно оставить выпуклую оболочку векторов &amp;lt;tex&amp;gt;v[j]&amp;lt;/tex&amp;gt;, а ответ на запрос - это поиск &amp;lt;tex&amp;gt;v[j]&amp;lt;/tex&amp;gt;, максимизирующего проекцию на &amp;lt;tex&amp;gt;u[i]&amp;lt;/tex&amp;gt;. Это задача поиска ближайшей точки выпуклого многоугольника (составленного из точек выпуклой оболочки) к заданной прямой (из &amp;lt;tex&amp;gt;(0, 0)&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;(1, a[i])&amp;lt;/tex&amp;gt;).  Ее можно решить за &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; двумя бинарными или одним тернарным поиском&lt;br /&gt;
Асимптотика алгоритма по-прежнему составит &amp;lt;tex&amp;gt;O(n \cdot \log(n))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Файл:picture5convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
Докажем то, что описанный выше алгоритм корректен. Для этого достаточно показать, что если имеются &amp;lt;math&amp;gt;3&amp;lt;/math&amp;gt; вектора &amp;lt;math&amp;gt;a, b, c&amp;lt;/math&amp;gt;, расположенные как на рис. 5, т.е. точка &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; не лежит на выпуклой оболочке векторов &amp;lt;tex&amp;gt;0, a, b, c &amp;lt;/tex&amp;gt; : &amp;lt;tex&amp;gt; \Leftrightarrow |[a-b, b-c]| &amp;lt; 0 &amp;lt;/tex&amp;gt;, то либо  &amp;lt;tex&amp;gt;(a, u[i])&amp;lt;/tex&amp;gt; оптимальнее, чем &amp;lt;tex&amp;gt;(b, u[i])&amp;lt;/tex&amp;gt;,  либо &amp;lt;tex&amp;gt;(c, u[i])&amp;lt;/tex&amp;gt; оптимальнее, чем &amp;lt;tex&amp;gt;(b, u[i])&amp;lt;/tex&amp;gt;.&lt;br /&gt;
{{Теорема&lt;br /&gt;
|id=th12392. &lt;br /&gt;
|statement=Если есть &amp;lt;tex&amp;gt;3&amp;lt;/tex&amp;gt; вектора &amp;lt;tex&amp;gt;a, b, c&amp;lt;/tex&amp;gt;, таких что &amp;lt;tex&amp;gt;|[a-b, b-c]| &amp;lt; 0&amp;lt;/tex&amp;gt; то либо &amp;lt;math&amp;gt;(a, u) &amp;lt; (b, u)&amp;lt;/math&amp;gt;, либо &amp;lt;math&amp;gt;(c, u) &amp;lt; (b, u)&amp;lt;/math&amp;gt;, где вектор &amp;lt;math&amp;gt;u = (1; k)&amp;lt;/math&amp;gt;.&lt;br /&gt;
|proof=По условию теоремы известно, что &amp;lt;tex&amp;gt;|[a-b, b-c]| &amp;lt; 0 \Leftrightarrow (a_{x} - b_{x})\cdot(b_{y} - c_{y}) &amp;lt; (a_{y} - b_{y}) \cdot (b_{x} - c_{x})&amp;lt;/tex&amp;gt; (*). Предположим (от противного), что &amp;lt;tex&amp;gt;(b, u) &amp;lt; (a, u) \Leftrightarrow b_{x}  + k \cdot b_{y} &amp;lt; c_{x} + k \cdot c_{y} \Leftrightarrow (b_{x} - c_{x}) &amp;lt; k \cdot (c_{y} - b_{y})&amp;lt;/tex&amp;gt; и  &amp;lt;tex&amp;gt;(b, u) &amp;lt; (c, u) \Leftrightarrow b_{x}  + k \cdot b_{y} &amp;lt; a_{x} + k \cdot a_{y} \Leftrightarrow (a_{x} - b_{x}) &amp;gt; k \cdot (b_{y} - a_{y})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Подставим эти неравенства в (*). Получим цепочку неравенств : &amp;lt;tex&amp;gt;k \cdot (a_{y} - b_{y})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (c_{y} - b_{y}) = k&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (b_{y} - a_{y}) \cdot &amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt;(b_{y} - c_{y})&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt; &amp;lt; (a_{x} - b_{x})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (b_{y} - c_{y})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; &amp;lt; (a_{y} - b_{y}) \cdot &amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt;(b_{x} - c_{x})&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;&amp;lt; k \cdot (a_{y} - b_{y})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (c_{y} - b_{y})&amp;lt;/tex&amp;gt;. Получили противоречие : &amp;lt;tex&amp;gt;k \cdot (a_{y} - b_{y}) \cdot (c_{y} - b_{y}) &amp;lt; k \cdot (a_{y} - b_{y}) \cdot (c_{y} - b_{y})&amp;lt;/tex&amp;gt;. Значит предположение неверно, чтд.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Из доказанной теоремы и следует корректность алгоритма.&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
1) http://neerc.ifmo.ru/wiki/index.php?title=Статические_выпуклые_оболочки:_Джарвис,_Грэхем,_Эндрю,_Чен,_QuickHull&lt;br /&gt;
&lt;br /&gt;
2) http://neerc.ifmo.ru/wiki/index.php?title=Динамическое_программирование&lt;br /&gt;
&lt;br /&gt;
[[Категория:Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория: Динамическое программирование]]&lt;br /&gt;
[[Категория: Способы оптимизации методов динамического программирования]]&lt;/div&gt;</summary>
		<author><name>92.255.113.52</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=Convex_hull_trick&amp;diff=60031</id>
		<title>Convex hull trick</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=Convex_hull_trick&amp;diff=60031"/>
				<updated>2017-01-18T17:16:53Z</updated>
		
		<summary type="html">&lt;p&gt;92.255.113.52: /* Альтернативный подход */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Convex hull trick -- один из методов оптимизации динамического программирования [[http://neerc.ifmo.ru/wiki/index.php?title=Динамическое_программирование]], использующий идею выпуклой оболочки. Позволяет улучшить ассимптотику решения некоторых задачь, решемых методом динамического программирования с &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt; до &amp;lt;tex&amp;gt;O(n\cdot\log(n))&amp;lt;/tex&amp;gt;. Техника впервые появилась в 1995 году (задачу на нее предложили в USACO -- национальной олимпиаде США по программированию). Массовую известность получила после IOI (международной олимпиады по программированию для школьников) 2002.&lt;br /&gt;
&lt;br /&gt;
==Пример задачи, решаемой методом convex hull trick==&lt;br /&gt;
Рассмотрим задачу на ДП:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; деревьев с высотами &amp;lt;tex&amp;gt;a_1, a_2, \dots, a_n&amp;lt;/tex&amp;gt; (в метрах). Требуется спилить их все, потратив минимальное количество монет на заправку&lt;br /&gt;
бензопилы. Но пила устроена так, что она может спиливать только по 1 метру от дерева, к которому ее применили. Также после&lt;br /&gt;
срубленного метра (любого дерева) пилу нужно заправлять, платя за  бензин определенной кол-во монет. Причем стоимость &lt;br /&gt;
бензина зависит от срубленных (полностью) деревьев. Если сейчас максимальный индекс срубленного дерева равен &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, то цена заправки &lt;br /&gt;
равна &amp;lt;tex&amp;gt;c_i&amp;lt;/tex&amp;gt;.  Изначально пила заправлена.&lt;br /&gt;
Также известны следующие ограничения : &amp;lt;tex&amp;gt;c_n = 0, a_1 = 1, a_i&amp;lt;/tex&amp;gt; возрастают, &amp;lt;tex&amp;gt;c_i&amp;lt;/tex&amp;gt; убывают. Изначально пила заправлена.&lt;br /&gt;
(убывание и возрастание нестрогие)&lt;br /&gt;
}}&lt;br /&gt;
(Задача H с Санкт-Петербургских сборов к РОИ 2016[http://neerc.ifmo.ru/school/camp-2016/problems/20160318a.pdf])&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
&amp;lt;includeonly&amp;gt;{{#if: {{{neat|}}}|&lt;br /&gt;
&amp;lt;div style=&amp;quot;background-color: #fcfcfc; float:left;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;background-color: #ddd;&amp;quot;&amp;gt;'''Задача:'''&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;border:1px dashed #2f6fab; padding: 8px; font-style: italic;&amp;quot;&amp;gt;{{{definition}}}&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;|&lt;br /&gt;
&amp;lt;table border=&amp;quot;0&amp;quot; width=&amp;quot;100%&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td style=&amp;quot;background-color: #ddd&amp;quot;&amp;gt;'''Задача:'''&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td style=&amp;quot;border:1px dashed #2f6fab; padding: 8px; background-color: #fcfcfc; font-style: italic;&amp;quot;&amp;gt;{{{definition}}}&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;}}&lt;br /&gt;
&amp;lt;/includeonly&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Наивное решение==&lt;br /&gt;
Сначала заметим важный факт : т.к. &amp;lt;tex&amp;gt;c[i]&amp;lt;/tex&amp;gt; убывают (нестрого) и &amp;lt;tex&amp;gt;c[n] = 0&amp;lt;/tex&amp;gt;, то все &amp;lt;tex&amp;gt;c[i]&amp;lt;/tex&amp;gt; неотрицательны.&lt;br /&gt;
Понятно, что нужно затратив минимальную стоимость срубить последнее (&amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;-е) дерево, т.к. после него все деревья можно будет рубить бесплатно (т.к. &amp;lt;tex&amp;gt;c[n] = 0&amp;lt;/tex&amp;gt;). Посчитаем следующую динамику : &amp;lt;tex&amp;gt;dp[i]&amp;lt;/tex&amp;gt; - минимальная стоимость, заплатив которую можно добиться того, что дерево номер &amp;lt;tex&amp;gt;i.&amp;lt;/tex&amp;gt; будет срублено.&lt;br /&gt;
База динамики : &amp;lt;tex&amp;gt;dp[1] = 0&amp;lt;/tex&amp;gt;, т.к. изначально пила заправлена и высота первого дерева равна 1, по условию задачи.&lt;br /&gt;
Переход динамики :  понятно, что выгодно рубить сначала более дорогие и низкие деревья, а потом более высокие и дешевые (док-во этого факта оставляется читателям как несложное упражнение, т.к. эта идея относится скорее к теме жадных алгоритмнов, чем к теме данной статьи). Поэтому перед  &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-м деревом мы обязательно срубили какое-то &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-е, причем &amp;lt;tex&amp;gt;j \leqslant i - 1&amp;lt;/tex&amp;gt;. Поэтому чтобы найти &amp;lt;tex&amp;gt;dp[i]&amp;lt;/tex&amp;gt; нужно перебрать все &amp;lt;tex&amp;gt;1 \leqslant j \leqslant i - 1&amp;lt;/tex&amp;gt; и попытаться использовать ответ для дерева намер &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;. Итак, пусть перед &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-м деревом мы полностью срубили &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-е, причем высота &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева составляет &amp;lt;tex&amp;gt;a[i]&amp;lt;/tex&amp;gt;, а т.к. последнее дерево, которое мы срубили имеет индекс &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, то стоимость каждого метра &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева составит &amp;lt;tex&amp;gt;c[j]&amp;lt;/tex&amp;gt;.  Поэтому на сруб &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева мы потратим &amp;lt;tex&amp;gt;a[i] \cdot c[j]&amp;lt;/tex&amp;gt; монет. Также не стоит забывать, ситуацию, когда  &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-е дерево полностью срублено, мы получили не бесплатно, а за &amp;lt;tex&amp;gt;dp[j]&amp;lt;/tex&amp;gt; монет.&lt;br /&gt;
Итогвая формула пересчета : &amp;lt;tex&amp;gt;dp[i] = \min\limits_{j=1...i-1} (dp[j] + a[i] \cdot c[j])&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Посмотрим на код выше описанного решения:&lt;br /&gt;
 '''int''' &amp;lt;tex&amp;gt;\mathtt{simpleDP}&amp;lt;/tex&amp;gt;('''int''' a[n], '''int''' c[n])&lt;br /&gt;
    dp[1] = 0&lt;br /&gt;
    dp[2] = dp[3] = ... = dp[n] = &amp;lt;tex&amp;gt;\infty&amp;lt;/tex&amp;gt;&lt;br /&gt;
    '''for'''  i = 1..n-1  &lt;br /&gt;
       dp[i] = &amp;lt;tex&amp;gt;+\infty&amp;lt;/tex&amp;gt;&lt;br /&gt;
       '''for''' j = 0..i-1 &lt;br /&gt;
           '''if''' (dp[j] + a[i] &amp;lt;tex&amp;gt;\cdot&amp;lt;/tex&amp;gt; c[j] &amp;lt; dp[i])&lt;br /&gt;
           dp[i] = dp[j] + a[i] &amp;lt;tex&amp;gt;\cdot&amp;lt;/tex&amp;gt; c[j]&lt;br /&gt;
 '''return''' dp[n]&lt;br /&gt;
Нетрудно видеть, что такая динамика работает за &amp;lt;tex&amp;gt;O(n^2)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Ключевая идея оптимизации==&lt;br /&gt;
Для начала сделаем замену обозначений. Давайте обозначим &amp;lt;tex&amp;gt;dp[j]&amp;lt;/tex&amp;gt; за &amp;lt;tex&amp;gt;b[j]&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;a[i]&amp;lt;/tex&amp;gt; за &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt;, а &amp;lt;tex&amp;gt;c[j]&amp;lt;/tex&amp;gt; за &amp;lt;tex&amp;gt;k[j]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь формула приняла вид &amp;lt;tex&amp;gt;dp[i] = \min\limits_{j=0...i-1}(k[j] \cdot x[i] + b[j])&amp;lt;/tex&amp;gt;. Выражение &amp;lt;tex&amp;gt;k[j] \cdot x + b[j]&amp;lt;/tex&amp;gt; - это в точности уравнение прямой вида &amp;lt;tex&amp;gt;y = kx + b&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сопоставим каждому &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, обработанному ранее, прямую &amp;lt;tex&amp;gt;y[j](x) = k[j] \cdot x + b[j]&amp;lt;/tex&amp;gt;. Из условия «&amp;lt;tex&amp;gt;c[i]&amp;lt;/tex&amp;gt; убывают &amp;lt;tex&amp;gt;\Leftrightarrow  k[j]&amp;lt;/tex&amp;gt; уменьшаются с номером &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;» следует то, что прямые, полученные ранее отсортированы в порядке убывания углового коэффициент. Давайте нарисуем несколько таких прямых :&lt;br /&gt;
&lt;br /&gt;
[[Файл:picture1convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
Выделим множество точек &amp;lt;tex&amp;gt;(x0, y0)&amp;lt;/tex&amp;gt; , таких что все они принадлежат одной из прямых и при этом нету ни одной прямой &amp;lt;tex&amp;gt;y’(x)&amp;lt;/tex&amp;gt;, такой что &amp;lt;tex&amp;gt;y’(x0) &amp;lt; y0&amp;lt;/tex&amp;gt;. Иными словами возьмем «выпуклую (вверх) оболочку» нашего множества прямых (её еще называют нижней ошибающей множества прямых на плоскости). Назовем ее «&amp;lt;tex&amp;gt;y = convex(x)&amp;lt;/tex&amp;gt;». Видно, что множество точек &amp;lt;math&amp;gt;(x, convex(x))&amp;lt;/math&amp;gt; представляет собой выпуклую вверх функцию.&lt;br /&gt;
&lt;br /&gt;
==Цель нижней огибающей множества прямых==&lt;br /&gt;
Пусть мы считаем динамику для &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева. Его задает &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt;. Итак, нам нужно для данного &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt; найти &amp;lt;tex&amp;gt;\min\limits_{j=0..i-1}(k[j] \cdot x[i] + b[i]) = \min\limits_{j=0..i-1}(y[j](x[i]))&amp;lt;/tex&amp;gt;. Это выражение есть &amp;lt;math&amp;gt;convex(x[i])&amp;lt;/math&amp;gt;. Из монотонности угловых коэффицентов отрезков, задающих выпуклую оболочку, и их расположения по координаты x следует то, что отрезок, который пересекает прямую &amp;lt;tex&amp;gt;x = x[i]&amp;lt;/tex&amp;gt;, можно найти бинарным поиском. Это потребует &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; времени на поиск такого &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;dp[i] = k[j] \cdot x[i] + b[j]&amp;lt;/tex&amp;gt;. Теперь осталось научиться поддерживать множество прямых и быстро добавлять &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-ю прямую после того, как мы посчитали &amp;lt;tex&amp;gt;b[i] = dp[i]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Воспользуемся идеей алгоритма построения выпуклой оболочки множества точек. Заведем 2 стека &amp;lt;tex&amp;gt;k[]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;b[]&amp;lt;/tex&amp;gt;, которые задают прямые в отсортированном порядке их угловыми коэффицентами и свободными членами. Рассмотрим ситуацию, когда мы хотим добавить новую (&amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-тую) прямую в множество. Пусть сейчас в множестве лежит &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt; прямых (нумерация с 1). Пусть &amp;lt;tex&amp;gt;(xL, yL)&amp;lt;/tex&amp;gt; - точка пересечения &amp;lt;tex&amp;gt;sz - 1&amp;lt;/tex&amp;gt;-й прямой множества и &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-й, а &amp;lt;tex&amp;gt;(xR, yR)&amp;lt;/tex&amp;gt; - точка пересечения новой прямой, которую мы хотим добавить в конец множества и &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-й. Нас будут интересовать только их &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;-овые координаты &amp;lt;tex&amp;gt;xL&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;xR&amp;lt;/tex&amp;gt;, соответственно. Если оказалось, что новая прямая пересекает &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-ю прямую выпуклой оболочки позже, чем &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-я &amp;lt;tex&amp;gt;sz - 1&amp;lt;/tex&amp;gt;-ю, т.е. &amp;lt;tex&amp;gt;(xL \geqslant xR)&amp;lt;/tex&amp;gt;, то &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-ю удалим из нашего множества, иначе - остановимся. Так будем делать, пока либо кол-во прямых в стеке не станет равным 2, либо &amp;lt;tex&amp;gt;xL&amp;lt;/tex&amp;gt; не станет меньше &amp;lt;tex&amp;gt;xR.&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Асимптотика : аналогично обычному алгоритму построения выпуклой оболочки, каждая прямая ровно &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; раз добавится в стек и максимум &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; раз удалится. Значит время работы перестройки выпуклой оболочки займет &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; суммарно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:picture2convexhull.png]]&lt;br /&gt;
[[Файл:picture3convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
{{Теорема&lt;br /&gt;
|id=th1239. &lt;br /&gt;
|statement=Алгоритм построения нижней огибающей множества прямых корректен.&lt;br /&gt;
|proof=Достаточно показать, что последнюю прямую нужно удалить из множества т.и т.т., когда она наша новая прямая пересекает ее в точке с координатой по оси X, меньшей, чем последняя - предпоследнюю.&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;Y(x) = Kx + B&amp;lt;/tex&amp;gt; - уравнение новой прямой,  &amp;lt;tex&amp;gt;y[i](x) = K[i]x + B[i]&amp;lt;/tex&amp;gt; - уравнения прямых множества. Тогда т.к. &amp;lt;tex&amp;gt;K &amp;lt; K[sz]&amp;lt;/tex&amp;gt;, то при &amp;lt;tex&amp;gt;x \in [- \infty; xR]  : y[sz](x) &amp;lt;= Y(x)&amp;lt;/tex&amp;gt;, а т.к. &amp;lt;tex&amp;gt; K[sz] &amp;lt; K[sz - 1]&amp;lt;/tex&amp;gt;, то при &amp;lt;tex&amp;gt;x \in [xL; + \infty]  : y[sz - 1](x) \geqslant y[sz](x)&amp;lt;/tex&amp;gt;. Если &amp;lt;tex&amp;gt;xL &amp;lt; xR&amp;lt;/tex&amp;gt;, то при &amp;lt;tex&amp;gt;x \in [xL; xR]  : y[sz - 1] \geqslant y[sz](x) и Y(x) \geqslant y[sz](x)&amp;lt;/tex&amp;gt;, т.е. на отрезке &amp;lt;tex&amp;gt;[xL; xR]&amp;lt;/tex&amp;gt; прямая номер sz лежит ниже остальных и её нужно оставить в множестве. Если же &amp;lt;tex&amp;gt;xL &amp;gt; xR&amp;lt;/tex&amp;gt;, то она ниже всех на отрезке &amp;lt;tex&amp;gt;[xL; xR] = \varnothing &amp;lt;/tex&amp;gt;, т.е. её можно удалить из множества&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==Детали реализации:==&lt;br /&gt;
Будем хранить 2 массива : &amp;lt;tex&amp;gt;front[]&amp;lt;/tex&amp;gt; - &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;-координаты, начиная с которых прямые совпадают с выпуклой оболочкой (т.е. i-я прямая совпадает с выпуклой оболочкой текущего множества прямых при &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;[front[i]; front[i + 1])&amp;lt;/tex&amp;gt; ) и &amp;lt;tex&amp;gt;st[]&amp;lt;/tex&amp;gt; - номера деревьев, соответствующих прямым (т.е. &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-я прямая множества, где &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;[1; sz]&amp;lt;/tex&amp;gt; соответствует дереву номер &amp;lt;tex&amp;gt;sz[i]&amp;lt;/tex&amp;gt;). Также воспользуемся тем, что &amp;lt;tex&amp;gt;x[i] = a[i]&amp;lt;/tex&amp;gt; возрастают (по условию задачи), а значит мы можем искать первое такое &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;x[i] \geqslant front[j]&amp;lt;/tex&amp;gt; не бинарным поиском, а методом двух указателей за &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; операций суммарно. Также массив front[] можно хранить в целых числах, округляя х-координаты в сторону лежащих правее по оси x до ближайшего целого (*), т.к. на самом деле мы, считая динамику, подставляем в уравнения прямых только целые &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt;, а значит если &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt;-я прямая пересекается с &amp;lt;tex&amp;gt;k+1&amp;lt;/tex&amp;gt;-й в точке &amp;lt;tex&amp;gt;z +&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\alpha&amp;lt;/tex&amp;gt; (z-целое,  &amp;lt;tex&amp;gt;\alpha&amp;lt;/tex&amp;gt;  &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;[0;1)&amp;lt;/tex&amp;gt;), то мы будем подставлять в их уравнения &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt;  или &amp;lt;tex&amp;gt;z + 1&amp;lt;/tex&amp;gt;. Поэтому можно считать, что новая прямая начинает совпадать с выпуклой оболочкой, начиная с &amp;lt;tex&amp;gt;x = z+1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Реализация==&lt;br /&gt;
  '''int''' &amp;lt;tex&amp;gt;\mathtt{ConvexHullTrick}&amp;lt;/tex&amp;gt;('''int''' a[n], '''int''' c[n])&lt;br /&gt;
     st[1] = 1&lt;br /&gt;
     from[1] = -&amp;lt;tex&amp;gt;\infty&amp;lt;/tex&amp;gt;&amp;lt;font color=green&amp;gt;// первая прямая покрывает все x-ы, начиная с -∞ &amp;lt;/font&amp;gt;&lt;br /&gt;
     sz = 1 &amp;lt;font color=green&amp;gt;// текущий размер выпуклой оболочки &amp;lt;/font&amp;gt;&lt;br /&gt;
     pos = 1 &amp;lt;font color=green&amp;gt;// текущая позиция первого такого j, что x[i] \geqslant front[st[j]] &amp;lt;/font &amp;gt;&lt;br /&gt;
     '''for'''  i = 2..n  &lt;br /&gt;
          '''while''' (front[pos] &amp;lt; x[i]) &amp;lt;font color=green&amp;gt;// метод 1 указателя (ищем первое pos, такое что x[i] покрывается &amp;quot;областью действия&amp;quot; st[pos]-той прямой &amp;lt;/font &amp;gt;&lt;br /&gt;
               pos = pos + 1&lt;br /&gt;
          j = st[pos]&lt;br /&gt;
          dp[i] = K[j]&amp;lt;math&amp;gt;\cdot&amp;lt;/math&amp;gt;a[i] + B[j] &lt;br /&gt;
          '''if''' (i &amp;lt; n)   &amp;lt;font color=green&amp;gt;// если у нас добавляется НЕ последняя прямая, то придется пересчитать выпуклую оболочку &amp;lt;/font &amp;gt;&lt;br /&gt;
                K[i] = c[i]  &amp;lt;font color=green&amp;gt;// наши переобозначения переменных &amp;lt;/font &amp;gt;&lt;br /&gt;
                B[i] = dp[i] &amp;lt;font color=green&amp;gt;// наши переобозначения переменных &amp;lt;/font &amp;gt;&lt;br /&gt;
                x = -&amp;lt;tex&amp;gt;\infty&amp;lt;/tex&amp;gt; &lt;br /&gt;
                '''while''' ''true'' &lt;br /&gt;
                      j = st[sz]&lt;br /&gt;
                      x = divide(B[j] - B[i], K[i] - K[j]) &amp;lt;font color=green&amp;gt;// x-координата пересечения с последней прямой оболочки, округленное в нужную сторону (*) &amp;lt;/font &amp;gt;&lt;br /&gt;
                      '''if''' (x &amp;gt; from[sz]) '''break'''  &amp;lt;font color=green&amp;gt;// перестаем удалять последнюю прямую из множества, если новая прямая пересекает ее позже, чем начинается ее &amp;quot;область действия&amp;quot; &amp;lt;/font &amp;gt;&lt;br /&gt;
                      sz = sz - 1&amp;lt;font color=green&amp;gt;// удаляем последнюю прямую, если она лишняя &amp;lt;/font &amp;gt;&lt;br /&gt;
                 st[sz + 1] = i  &lt;br /&gt;
                 from[sz + 1] = x &amp;lt;font color=green&amp;gt;// добавили новую прямую &amp;lt;/font &amp;gt;&lt;br /&gt;
                 sz = sz + 1&lt;br /&gt;
  '''return''' dp[n] &lt;br /&gt;
Здесь функция &amp;lt;tex&amp;gt;\mathtt{divide}&amp;lt;/tex&amp;gt;(a, b) возвращает нужное(*) округление a / b. Приведем её код :&lt;br /&gt;
 '''int''' &amp;lt;tex&amp;gt;\mathtt{divide}&amp;lt;/tex&amp;gt;('''int''' a, '''int''' b)&lt;br /&gt;
        delta = 0&lt;br /&gt;
        '''if''' (a '''mod''' b ≠ 0) delta = 1&lt;br /&gt;
        '''if''' ((a &amp;gt; 0 '''and''' b &amp;gt; 0) '''or''' (a &amp;lt; 0 '''and''' b &amp;lt; 0)) '''return''' [a / b] + delta&lt;br /&gt;
 '''return''' -[|a| / |b|]&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
Такая реализация будет работать за O(n).&lt;br /&gt;
&lt;br /&gt;
==Динамический convex hull trick==&lt;br /&gt;
	Заметим, что условия на прямые, что &amp;lt;tex&amp;gt;k[i]&amp;lt;/tex&amp;gt; возрастает/убывает и &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt; убывает/возрастает выглядят достаточно редкими для большинства задач. Пусть в задаче таких ограничений нет. Первый способ борьбы с этой проблемой - отсортировать входные данные нужным образом, не испортив свойств задачи (пример : задача G c Санкт-Петербургских сборов к РОИ 2016[http://neerc.ifmo.ru/school/camp-2016/problems/20160318a.pdf]).&lt;br /&gt;
&lt;br /&gt;
	Но рассмотрим общий случай. По-прежнему у нас есть выпуклая оболочка прямых, имея которую мы за &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; можем найти   &amp;lt;tex&amp;gt;dp[i]&amp;lt;/tex&amp;gt;, но теперь вставку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-й прямой в оболочку уже нельзя выполнить описанным ранее способом за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt; в среднем. У нас есть выпуклая оболочка, наша прямая пересекает ее, возможно, «отсекая» несколько отрезков выпуклой оболочки в середине (рис. 4 : красная прямая - та, которую мы хотим вставить в наше множество). Более формально : теперь наша новая прямая будет ниже остальных при &amp;lt;tex&amp;gt;x \in [x1; x2]&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;x1, x2 \in R&amp;lt;/tex&amp;gt; - точки пересечения с некоторыми прямыми, причем &amp;lt;tex&amp;gt;x2&amp;lt;/tex&amp;gt; не обязательно равно &amp;lt;tex&amp;gt;+ \infty&amp;lt;/tex&amp;gt;&lt;br /&gt;
[[Файл:picture4convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
Чтобы уметь вставлять прямую в множество будем хранить &amp;lt;math&amp;gt;std::set&amp;lt;/math&amp;gt; (или любой аналог в других языках программирования) пар &amp;lt;tex&amp;gt;(k, st)&amp;lt;/tex&amp;gt; =  &amp;lt;tex&amp;gt;(коэффицент прямой, ее номер в глобальной нумерации)&amp;lt;/tex&amp;gt;. Когда приходит новая прямая, ищем последнюю прямую с меньшим угловым коэффицентом, чем у той прямой, которую мы хотим добавить в множество. Поиск такой прямой занимает &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt;. Начиная с найденной прямой выполняем &amp;quot;старый&amp;quot; алгоритм (удаляем, пока текущая прямая множества бесполезна). И симметричный алгоритм применяем ко всем прямым справа от нашей (удаляем правого соседа нашей прямой, пока она пересекает нас позже, чем своего правого соседа).&lt;br /&gt;
	&lt;br /&gt;
Асимптотика решения составит &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; на каждый из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; запросов «добавить прямую» + &amp;lt;tex&amp;gt;O(n\cdot\log(n))&amp;lt;/tex&amp;gt; суммарно на удаление прямых, т.к. по-прежнему каждая прямая не более одного раза удалится из множества, а каждое удаление из std::set занимает &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; времени. Итого &amp;lt;math&amp;gt;O(n\cdot\log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Альтернативный подход ==&lt;br /&gt;
Другой способ интерпретировать выражение &amp;lt;tex&amp;gt;dp[i] = \min\limits_{j=0...i-1}(c[j] \cdot a[i] + dp[j])&amp;lt;/tex&amp;gt;  заключается в том, что мы будем пытаться свести задачу к стандартной выпуклой оболочке множества точек. Перепишем выражение средующим образом : &amp;lt;tex&amp;gt;dp[j] + a[i] \cdot c[j] = (dp[j], c[j]) \cdot (1, a[i])&amp;lt;/tex&amp;gt;, т.е. запишем как скалярное произведение векторов &amp;lt;tex&amp;gt;v[j] = (dp[j], c[j])&amp;lt;/tex&amp;gt; и &amp;lt;tex &amp;gt;u[i] = (1, a[i])&amp;lt;/tex &amp;gt;. Вектора  &amp;lt;tex &amp;gt;v[j] =  (dp[j], c[j])&amp;lt;/tex&amp;gt; хотелось бы организовать так, чтобы за &amp;lt;tex &amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; находить вектор, максимизирующий выражение &amp;lt;tex&amp;gt;v[j] \cdot u[i]&amp;lt;/tex&amp;gt;. Посмотрим на рис. 5. Заметим интуитивно очевидный факт : красная точка (вектор) &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; не может давать более оптимальное значение &amp;lt;tex&amp;gt;v[j] \cdot u[i]&amp;lt;/tex&amp;gt; одновременно чем обе синие точки. По этой причине нам достаточно оставить выпуклую оболочку векторов &amp;lt;tex&amp;gt;v[j]&amp;lt;/tex&amp;gt;, а ответ на запрос - это поиск &amp;lt;tex&amp;gt;v[j]&amp;lt;/tex&amp;gt;, максимизирующего проекцию на &amp;lt;tex&amp;gt;u[i]&amp;lt;/tex&amp;gt;. Это задача поиска ближайшей точки выпуклого многоугольника (составленного из точек выпуклой оболочки) к заданной прямой (из &amp;lt;tex&amp;gt;(0, 0)&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;(1, a[i])&amp;lt;/tex&amp;gt;).  Ее можно решить за &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; двумя бинарными или одним тернарным поиском&lt;br /&gt;
Асимптотика алгоритма по-прежнему составит &amp;lt;tex&amp;gt;O(n \cdot \log(n))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Файл:picture5convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
Докажем то, что описанный выше алгоритм корректен. Для этого достаточно показать, что если имеются &amp;lt;math&amp;gt;3&amp;lt;/math&amp;gt; вектора &amp;lt;math&amp;gt;a, b, c&amp;lt;/math&amp;gt;, расположенные как на рис. 5, т.е. точка &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; не лежит на выпуклой оболочке векторов &amp;lt;tex&amp;gt;0, a, b, c &amp;lt;/tex&amp;gt; : &amp;lt;tex&amp;gt; \Leftrightarrow |[a-b, b-c]| &amp;lt; 0 &amp;lt;/tex&amp;gt;, то либо  &amp;lt;tex&amp;gt;(a, u[i])&amp;lt;/tex&amp;gt; оптимальнее, чем &amp;lt;tex&amp;gt;(b, u[i])&amp;lt;/tex&amp;gt;,  либо &amp;lt;tex&amp;gt;(c, u[i])&amp;lt;/tex&amp;gt; оптимальнее, чем &amp;lt;tex&amp;gt;(b, u[i])&amp;lt;/tex&amp;gt;.&lt;br /&gt;
{{Теорема&lt;br /&gt;
|id=th12392. &lt;br /&gt;
|statement=Если есть &amp;lt;tex&amp;gt;3&amp;lt;/tex&amp;gt; вектора &amp;lt;tex&amp;gt;a, b, c&amp;lt;/tex&amp;gt;, таких что &amp;lt;tex&amp;gt;|[a-b, b-c]| &amp;lt; 0&amp;lt;/tex&amp;gt; то либо &amp;lt;math&amp;gt;(a, u) &amp;lt; (b, u)&amp;lt;/math&amp;gt;, либо &amp;lt;math&amp;gt;(c, u) &amp;lt; (b, u)&amp;lt;/math&amp;gt;, где вектор &amp;lt;math&amp;gt;u = (1; k)&amp;lt;/math&amp;gt;.&lt;br /&gt;
|proof=По условию теоремы известно, что &amp;lt;tex&amp;gt;|[a-b, b-c]| &amp;lt; 0 \Leftrightarrow (a_{x} - b_{x})\cdot(b_{y} - c_{y}) &amp;lt; (a_{y} - b_{y}) \cdot (b_{x} - c_{x})&amp;lt;/tex&amp;gt; (*). Предположим (от противного), что &amp;lt;tex&amp;gt;(b, u) &amp;lt; (a, u) \Leftrightarrow b_{x}  + k \cdot b_{y} &amp;lt; c_{x} + k \cdot c_{y} \Leftrightarrow (b_{x} - c_{x}) &amp;lt; k \cdot (c_{y} - b_{y})&amp;lt;/tex&amp;gt; и  &amp;lt;tex&amp;gt;(b, u) &amp;lt; (c, u) \Leftrightarrow b_{x}  + k \cdot b_{y} &amp;lt; a_{x} + k \cdot a_{y} \Leftrightarrow (a_{x} - b_{x}) &amp;gt; k \cdot (b_{y} - a_{y})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Подставим эти неравенства в (*). Получим цепочку неравенств : &amp;lt;tex&amp;gt;k \cdot (a_{y} - b_{y})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (c_{y} - b_{y}) = k&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (b_{y} - a_{y}) \cdot &amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt;(b_{y} - c_{y})&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt; &amp;lt; (a_{x} - b_{x})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (b_{y} - c_{y})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; &amp;lt; (a_{y} - b_{y}) \cdot &amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt;(b_{x} - c_{x})&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;&amp;lt; k \cdot (a_{y} - b_{y})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (c_{y} - b_{y})&amp;lt;/tex&amp;gt;. Получили противоречие : &amp;lt;tex&amp;gt;k \cdot (a_{y} - b_{y}) \cdot (c_{y} - b_{y}) &amp;lt; k \cdot (a_{y} - b_{y}) \cdot (c_{y} - b_{y})&amp;lt;/tex&amp;gt;. Значит предположение неверно, чтд.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Из доказанной теоремы и следует корректность алгоритма.&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
1) http://neerc.ifmo.ru/wiki/index.php?title=Статические_выпуклые_оболочки:_Джарвис,_Грэхем,_Эндрю,_Чен,_QuickHull&lt;br /&gt;
&lt;br /&gt;
2) http://neerc.ifmo.ru/wiki/index.php?title=Динамическое_программирование&lt;br /&gt;
&lt;br /&gt;
[[Категория:Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория: Динамическое программирование]]&lt;br /&gt;
[[Категория: Способы оптимизации методов динамического программирования]]&lt;/div&gt;</summary>
		<author><name>92.255.113.52</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=Convex_hull_trick&amp;diff=60029</id>
		<title>Convex hull trick</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=Convex_hull_trick&amp;diff=60029"/>
				<updated>2017-01-18T17:15:05Z</updated>
		
		<summary type="html">&lt;p&gt;92.255.113.52: /* Альтернативный подход */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Convex hull trick -- один из методов оптимизации динамического программирования [[http://neerc.ifmo.ru/wiki/index.php?title=Динамическое_программирование]], использующий идею выпуклой оболочки. Позволяет улучшить ассимптотику решения некоторых задачь, решемых методом динамического программирования с &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt; до &amp;lt;tex&amp;gt;O(n\cdot\log(n))&amp;lt;/tex&amp;gt;. Техника впервые появилась в 1995 году (задачу на нее предложили в USACO -- национальной олимпиаде США по программированию). Массовую известность получила после IOI (международной олимпиады по программированию для школьников) 2002.&lt;br /&gt;
&lt;br /&gt;
==Пример задачи, решаемой методом convex hull trick==&lt;br /&gt;
Рассмотрим задачу на ДП:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; деревьев с высотами &amp;lt;tex&amp;gt;a_1, a_2, \dots, a_n&amp;lt;/tex&amp;gt; (в метрах). Требуется спилить их все, потратив минимальное количество монет на заправку&lt;br /&gt;
бензопилы. Но пила устроена так, что она может спиливать только по 1 метру от дерева, к которому ее применили. Также после&lt;br /&gt;
срубленного метра (любого дерева) пилу нужно заправлять, платя за  бензин определенной кол-во монет. Причем стоимость &lt;br /&gt;
бензина зависит от срубленных (полностью) деревьев. Если сейчас максимальный индекс срубленного дерева равен &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, то цена заправки &lt;br /&gt;
равна &amp;lt;tex&amp;gt;c_i&amp;lt;/tex&amp;gt;.  Изначально пила заправлена.&lt;br /&gt;
Также известны следующие ограничения : &amp;lt;tex&amp;gt;c_n = 0, a_1 = 1, a_i&amp;lt;/tex&amp;gt; возрастают, &amp;lt;tex&amp;gt;c_i&amp;lt;/tex&amp;gt; убывают. Изначально пила заправлена.&lt;br /&gt;
(убывание и возрастание нестрогие)&lt;br /&gt;
}}&lt;br /&gt;
(Задача H с Санкт-Петербургских сборов к РОИ 2016[http://neerc.ifmo.ru/school/camp-2016/problems/20160318a.pdf])&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
&amp;lt;includeonly&amp;gt;{{#if: {{{neat|}}}|&lt;br /&gt;
&amp;lt;div style=&amp;quot;background-color: #fcfcfc; float:left;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;background-color: #ddd;&amp;quot;&amp;gt;'''Задача:'''&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;border:1px dashed #2f6fab; padding: 8px; font-style: italic;&amp;quot;&amp;gt;{{{definition}}}&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;|&lt;br /&gt;
&amp;lt;table border=&amp;quot;0&amp;quot; width=&amp;quot;100%&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td style=&amp;quot;background-color: #ddd&amp;quot;&amp;gt;'''Задача:'''&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td style=&amp;quot;border:1px dashed #2f6fab; padding: 8px; background-color: #fcfcfc; font-style: italic;&amp;quot;&amp;gt;{{{definition}}}&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;}}&lt;br /&gt;
&amp;lt;/includeonly&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Наивное решение==&lt;br /&gt;
Сначала заметим важный факт : т.к. &amp;lt;tex&amp;gt;c[i]&amp;lt;/tex&amp;gt; убывают (нестрого) и &amp;lt;tex&amp;gt;c[n] = 0&amp;lt;/tex&amp;gt;, то все &amp;lt;tex&amp;gt;c[i]&amp;lt;/tex&amp;gt; неотрицательны.&lt;br /&gt;
Понятно, что нужно затратив минимальную стоимость срубить последнее (&amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;-е) дерево, т.к. после него все деревья можно будет рубить бесплатно (т.к. &amp;lt;tex&amp;gt;c[n] = 0&amp;lt;/tex&amp;gt;). Посчитаем следующую динамику : &amp;lt;tex&amp;gt;dp[i]&amp;lt;/tex&amp;gt; - минимальная стоимость, заплатив которую можно добиться того, что дерево номер &amp;lt;tex&amp;gt;i.&amp;lt;/tex&amp;gt; будет срублено.&lt;br /&gt;
База динамики : &amp;lt;tex&amp;gt;dp[1] = 0&amp;lt;/tex&amp;gt;, т.к. изначально пила заправлена и высота первого дерева равна 1, по условию задачи.&lt;br /&gt;
Переход динамики :  понятно, что выгодно рубить сначала более дорогие и низкие деревья, а потом более высокие и дешевые (док-во этого факта оставляется читателям как несложное упражнение, т.к. эта идея относится скорее к теме жадных алгоритмнов, чем к теме данной статьи). Поэтому перед  &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-м деревом мы обязательно срубили какое-то &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-е, причем &amp;lt;tex&amp;gt;j \leqslant i - 1&amp;lt;/tex&amp;gt;. Поэтому чтобы найти &amp;lt;tex&amp;gt;dp[i]&amp;lt;/tex&amp;gt; нужно перебрать все &amp;lt;tex&amp;gt;1 \leqslant j \leqslant i - 1&amp;lt;/tex&amp;gt; и попытаться использовать ответ для дерева намер &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;. Итак, пусть перед &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-м деревом мы полностью срубили &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-е, причем высота &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева составляет &amp;lt;tex&amp;gt;a[i]&amp;lt;/tex&amp;gt;, а т.к. последнее дерево, которое мы срубили имеет индекс &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, то стоимость каждого метра &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева составит &amp;lt;tex&amp;gt;c[j]&amp;lt;/tex&amp;gt;.  Поэтому на сруб &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева мы потратим &amp;lt;tex&amp;gt;a[i] \cdot c[j]&amp;lt;/tex&amp;gt; монет. Также не стоит забывать, ситуацию, когда  &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-е дерево полностью срублено, мы получили не бесплатно, а за &amp;lt;tex&amp;gt;dp[j]&amp;lt;/tex&amp;gt; монет.&lt;br /&gt;
Итогвая формула пересчета : &amp;lt;tex&amp;gt;dp[i] = \min\limits_{j=1...i-1} (dp[j] + a[i] \cdot c[j])&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Посмотрим на код выше описанного решения:&lt;br /&gt;
 '''int''' &amp;lt;tex&amp;gt;\mathtt{simpleDP}&amp;lt;/tex&amp;gt;('''int''' a[n], '''int''' c[n])&lt;br /&gt;
    dp[1] = 0&lt;br /&gt;
    dp[2] = dp[3] = ... = dp[n] = &amp;lt;tex&amp;gt;\infty&amp;lt;/tex&amp;gt;&lt;br /&gt;
    '''for'''  i = 1..n-1  &lt;br /&gt;
       dp[i] = &amp;lt;tex&amp;gt;+\infty&amp;lt;/tex&amp;gt;&lt;br /&gt;
       '''for''' j = 0..i-1 &lt;br /&gt;
           '''if''' (dp[j] + a[i] &amp;lt;tex&amp;gt;\cdot&amp;lt;/tex&amp;gt; c[j] &amp;lt; dp[i])&lt;br /&gt;
           dp[i] = dp[j] + a[i] &amp;lt;tex&amp;gt;\cdot&amp;lt;/tex&amp;gt; c[j]&lt;br /&gt;
 '''return''' dp[n]&lt;br /&gt;
Нетрудно видеть, что такая динамика работает за &amp;lt;tex&amp;gt;O(n^2)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Ключевая идея оптимизации==&lt;br /&gt;
Для начала сделаем замену обозначений. Давайте обозначим &amp;lt;tex&amp;gt;dp[j]&amp;lt;/tex&amp;gt; за &amp;lt;tex&amp;gt;b[j]&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;a[i]&amp;lt;/tex&amp;gt; за &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt;, а &amp;lt;tex&amp;gt;c[j]&amp;lt;/tex&amp;gt; за &amp;lt;tex&amp;gt;k[j]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь формула приняла вид &amp;lt;tex&amp;gt;dp[i] = \min\limits_{j=0...i-1}(k[j] \cdot x[i] + b[j])&amp;lt;/tex&amp;gt;. Выражение &amp;lt;tex&amp;gt;k[j] \cdot x + b[j]&amp;lt;/tex&amp;gt; - это в точности уравнение прямой вида &amp;lt;tex&amp;gt;y = kx + b&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сопоставим каждому &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, обработанному ранее, прямую &amp;lt;tex&amp;gt;y[j](x) = k[j] \cdot x + b[j]&amp;lt;/tex&amp;gt;. Из условия «&amp;lt;tex&amp;gt;c[i]&amp;lt;/tex&amp;gt; убывают &amp;lt;tex&amp;gt;\Leftrightarrow  k[j]&amp;lt;/tex&amp;gt; уменьшаются с номером &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;» следует то, что прямые, полученные ранее отсортированы в порядке убывания углового коэффициент. Давайте нарисуем несколько таких прямых :&lt;br /&gt;
&lt;br /&gt;
[[Файл:picture1convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
Выделим множество точек &amp;lt;tex&amp;gt;(x0, y0)&amp;lt;/tex&amp;gt; , таких что все они принадлежат одной из прямых и при этом нету ни одной прямой &amp;lt;tex&amp;gt;y’(x)&amp;lt;/tex&amp;gt;, такой что &amp;lt;tex&amp;gt;y’(x0) &amp;lt; y0&amp;lt;/tex&amp;gt;. Иными словами возьмем «выпуклую (вверх) оболочку» нашего множества прямых (её еще называют нижней ошибающей множества прямых на плоскости). Назовем ее «&amp;lt;tex&amp;gt;y = convex(x)&amp;lt;/tex&amp;gt;». Видно, что множество точек &amp;lt;math&amp;gt;(x, convex(x))&amp;lt;/math&amp;gt; представляет собой выпуклую вверх функцию.&lt;br /&gt;
&lt;br /&gt;
==Цель нижней огибающей множества прямых==&lt;br /&gt;
Пусть мы считаем динамику для &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева. Его задает &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt;. Итак, нам нужно для данного &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt; найти &amp;lt;tex&amp;gt;\min\limits_{j=0..i-1}(k[j] \cdot x[i] + b[i]) = \min\limits_{j=0..i-1}(y[j](x[i]))&amp;lt;/tex&amp;gt;. Это выражение есть &amp;lt;math&amp;gt;convex(x[i])&amp;lt;/math&amp;gt;. Из монотонности угловых коэффицентов отрезков, задающих выпуклую оболочку, и их расположения по координаты x следует то, что отрезок, который пересекает прямую &amp;lt;tex&amp;gt;x = x[i]&amp;lt;/tex&amp;gt;, можно найти бинарным поиском. Это потребует &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; времени на поиск такого &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;dp[i] = k[j] \cdot x[i] + b[j]&amp;lt;/tex&amp;gt;. Теперь осталось научиться поддерживать множество прямых и быстро добавлять &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-ю прямую после того, как мы посчитали &amp;lt;tex&amp;gt;b[i] = dp[i]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Воспользуемся идеей алгоритма построения выпуклой оболочки множества точек. Заведем 2 стека &amp;lt;tex&amp;gt;k[]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;b[]&amp;lt;/tex&amp;gt;, которые задают прямые в отсортированном порядке их угловыми коэффицентами и свободными членами. Рассмотрим ситуацию, когда мы хотим добавить новую (&amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-тую) прямую в множество. Пусть сейчас в множестве лежит &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt; прямых (нумерация с 1). Пусть &amp;lt;tex&amp;gt;(xL, yL)&amp;lt;/tex&amp;gt; - точка пересечения &amp;lt;tex&amp;gt;sz - 1&amp;lt;/tex&amp;gt;-й прямой множества и &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-й, а &amp;lt;tex&amp;gt;(xR, yR)&amp;lt;/tex&amp;gt; - точка пересечения новой прямой, которую мы хотим добавить в конец множества и &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-й. Нас будут интересовать только их &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;-овые координаты &amp;lt;tex&amp;gt;xL&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;xR&amp;lt;/tex&amp;gt;, соответственно. Если оказалось, что новая прямая пересекает &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-ю прямую выпуклой оболочки позже, чем &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-я &amp;lt;tex&amp;gt;sz - 1&amp;lt;/tex&amp;gt;-ю, т.е. &amp;lt;tex&amp;gt;(xL \geqslant xR)&amp;lt;/tex&amp;gt;, то &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-ю удалим из нашего множества, иначе - остановимся. Так будем делать, пока либо кол-во прямых в стеке не станет равным 2, либо &amp;lt;tex&amp;gt;xL&amp;lt;/tex&amp;gt; не станет меньше &amp;lt;tex&amp;gt;xR.&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Асимптотика : аналогично обычному алгоритму построения выпуклой оболочки, каждая прямая ровно &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; раз добавится в стек и максимум &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; раз удалится. Значит время работы перестройки выпуклой оболочки займет &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; суммарно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:picture2convexhull.png]]&lt;br /&gt;
[[Файл:picture3convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
{{Теорема&lt;br /&gt;
|id=th1239. &lt;br /&gt;
|statement=Алгоритм построения нижней огибающей множества прямых корректен.&lt;br /&gt;
|proof=Достаточно показать, что последнюю прямую нужно удалить из множества т.и т.т., когда она наша новая прямая пересекает ее в точке с координатой по оси X, меньшей, чем последняя - предпоследнюю.&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;Y(x) = Kx + B&amp;lt;/tex&amp;gt; - уравнение новой прямой,  &amp;lt;tex&amp;gt;y[i](x) = K[i]x + B[i]&amp;lt;/tex&amp;gt; - уравнения прямых множества. Тогда т.к. &amp;lt;tex&amp;gt;K &amp;lt; K[sz]&amp;lt;/tex&amp;gt;, то при &amp;lt;tex&amp;gt;x \in [- \infty; xR]  : y[sz](x) &amp;lt;= Y(x)&amp;lt;/tex&amp;gt;, а т.к. &amp;lt;tex&amp;gt; K[sz] &amp;lt; K[sz - 1]&amp;lt;/tex&amp;gt;, то при &amp;lt;tex&amp;gt;x \in [xL; + \infty]  : y[sz - 1](x) \geqslant y[sz](x)&amp;lt;/tex&amp;gt;. Если &amp;lt;tex&amp;gt;xL &amp;lt; xR&amp;lt;/tex&amp;gt;, то при &amp;lt;tex&amp;gt;x \in [xL; xR]  : y[sz - 1] \geqslant y[sz](x) и Y(x) \geqslant y[sz](x)&amp;lt;/tex&amp;gt;, т.е. на отрезке &amp;lt;tex&amp;gt;[xL; xR]&amp;lt;/tex&amp;gt; прямая номер sz лежит ниже остальных и её нужно оставить в множестве. Если же &amp;lt;tex&amp;gt;xL &amp;gt; xR&amp;lt;/tex&amp;gt;, то она ниже всех на отрезке &amp;lt;tex&amp;gt;[xL; xR] = \varnothing &amp;lt;/tex&amp;gt;, т.е. её можно удалить из множества&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==Детали реализации:==&lt;br /&gt;
Будем хранить 2 массива : &amp;lt;tex&amp;gt;front[]&amp;lt;/tex&amp;gt; - &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;-координаты, начиная с которых прямые совпадают с выпуклой оболочкой (т.е. i-я прямая совпадает с выпуклой оболочкой текущего множества прямых при &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;[front[i]; front[i + 1])&amp;lt;/tex&amp;gt; ) и &amp;lt;tex&amp;gt;st[]&amp;lt;/tex&amp;gt; - номера деревьев, соответствующих прямым (т.е. &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-я прямая множества, где &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;[1; sz]&amp;lt;/tex&amp;gt; соответствует дереву номер &amp;lt;tex&amp;gt;sz[i]&amp;lt;/tex&amp;gt;). Также воспользуемся тем, что &amp;lt;tex&amp;gt;x[i] = a[i]&amp;lt;/tex&amp;gt; возрастают (по условию задачи), а значит мы можем искать первое такое &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;x[i] \geqslant front[j]&amp;lt;/tex&amp;gt; не бинарным поиском, а методом двух указателей за &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; операций суммарно. Также массив front[] можно хранить в целых числах, округляя х-координаты в сторону лежащих правее по оси x до ближайшего целого (*), т.к. на самом деле мы, считая динамику, подставляем в уравнения прямых только целые &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt;, а значит если &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt;-я прямая пересекается с &amp;lt;tex&amp;gt;k+1&amp;lt;/tex&amp;gt;-й в точке &amp;lt;tex&amp;gt;z +&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\alpha&amp;lt;/tex&amp;gt; (z-целое,  &amp;lt;tex&amp;gt;\alpha&amp;lt;/tex&amp;gt;  &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;[0;1)&amp;lt;/tex&amp;gt;), то мы будем подставлять в их уравнения &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt;  или &amp;lt;tex&amp;gt;z + 1&amp;lt;/tex&amp;gt;. Поэтому можно считать, что новая прямая начинает совпадать с выпуклой оболочкой, начиная с &amp;lt;tex&amp;gt;x = z+1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Реализация==&lt;br /&gt;
  '''int''' &amp;lt;tex&amp;gt;\mathtt{ConvexHullTrick}&amp;lt;/tex&amp;gt;('''int''' a[n], '''int''' c[n])&lt;br /&gt;
     st[1] = 1&lt;br /&gt;
     from[1] = -&amp;lt;tex&amp;gt;\infty&amp;lt;/tex&amp;gt;&amp;lt;font color=green&amp;gt;// первая прямая покрывает все x-ы, начиная с -∞ &amp;lt;/font&amp;gt;&lt;br /&gt;
     sz = 1 &amp;lt;font color=green&amp;gt;// текущий размер выпуклой оболочки &amp;lt;/font&amp;gt;&lt;br /&gt;
     pos = 1 &amp;lt;font color=green&amp;gt;// текущая позиция первого такого j, что x[i] \geqslant front[st[j]] &amp;lt;/font &amp;gt;&lt;br /&gt;
     '''for'''  i = 2..n  &lt;br /&gt;
          '''while''' (front[pos] &amp;lt; x[i]) &amp;lt;font color=green&amp;gt;// метод 1 указателя (ищем первое pos, такое что x[i] покрывается &amp;quot;областью действия&amp;quot; st[pos]-той прямой &amp;lt;/font &amp;gt;&lt;br /&gt;
               pos = pos + 1&lt;br /&gt;
          j = st[pos]&lt;br /&gt;
          dp[i] = K[j]&amp;lt;math&amp;gt;\cdot&amp;lt;/math&amp;gt;a[i] + B[j] &lt;br /&gt;
          '''if''' (i &amp;lt; n)   &amp;lt;font color=green&amp;gt;// если у нас добавляется НЕ последняя прямая, то придется пересчитать выпуклую оболочку &amp;lt;/font &amp;gt;&lt;br /&gt;
                K[i] = c[i]  &amp;lt;font color=green&amp;gt;// наши переобозначения переменных &amp;lt;/font &amp;gt;&lt;br /&gt;
                B[i] = dp[i] &amp;lt;font color=green&amp;gt;// наши переобозначения переменных &amp;lt;/font &amp;gt;&lt;br /&gt;
                x = -&amp;lt;tex&amp;gt;\infty&amp;lt;/tex&amp;gt; &lt;br /&gt;
                '''while''' ''true'' &lt;br /&gt;
                      j = st[sz]&lt;br /&gt;
                      x = divide(B[j] - B[i], K[i] - K[j]) &amp;lt;font color=green&amp;gt;// x-координата пересечения с последней прямой оболочки, округленное в нужную сторону (*) &amp;lt;/font &amp;gt;&lt;br /&gt;
                      '''if''' (x &amp;gt; from[sz]) '''break'''  &amp;lt;font color=green&amp;gt;// перестаем удалять последнюю прямую из множества, если новая прямая пересекает ее позже, чем начинается ее &amp;quot;область действия&amp;quot; &amp;lt;/font &amp;gt;&lt;br /&gt;
                      sz = sz - 1&amp;lt;font color=green&amp;gt;// удаляем последнюю прямую, если она лишняя &amp;lt;/font &amp;gt;&lt;br /&gt;
                 st[sz + 1] = i  &lt;br /&gt;
                 from[sz + 1] = x &amp;lt;font color=green&amp;gt;// добавили новую прямую &amp;lt;/font &amp;gt;&lt;br /&gt;
                 sz = sz + 1&lt;br /&gt;
  '''return''' dp[n] &lt;br /&gt;
Здесь функция &amp;lt;tex&amp;gt;\mathtt{divide}&amp;lt;/tex&amp;gt;(a, b) возвращает нужное(*) округление a / b. Приведем её код :&lt;br /&gt;
 '''int''' &amp;lt;tex&amp;gt;\mathtt{divide}&amp;lt;/tex&amp;gt;('''int''' a, '''int''' b)&lt;br /&gt;
        delta = 0&lt;br /&gt;
        '''if''' (a '''mod''' b ≠ 0) delta = 1&lt;br /&gt;
        '''if''' ((a &amp;gt; 0 '''and''' b &amp;gt; 0) '''or''' (a &amp;lt; 0 '''and''' b &amp;lt; 0)) '''return''' [a / b] + delta&lt;br /&gt;
 '''return''' -[|a| / |b|]&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
Такая реализация будет работать за O(n).&lt;br /&gt;
&lt;br /&gt;
==Динамический convex hull trick==&lt;br /&gt;
	Заметим, что условия на прямые, что &amp;lt;tex&amp;gt;k[i]&amp;lt;/tex&amp;gt; возрастает/убывает и &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt; убывает/возрастает выглядят достаточно редкими для большинства задач. Пусть в задаче таких ограничений нет. Первый способ борьбы с этой проблемой - отсортировать входные данные нужным образом, не испортив свойств задачи (пример : задача G c Санкт-Петербургских сборов к РОИ 2016[http://neerc.ifmo.ru/school/camp-2016/problems/20160318a.pdf]).&lt;br /&gt;
&lt;br /&gt;
	Но рассмотрим общий случай. По-прежнему у нас есть выпуклая оболочка прямых, имея которую мы за &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; можем найти   &amp;lt;tex&amp;gt;dp[i]&amp;lt;/tex&amp;gt;, но теперь вставку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-й прямой в оболочку уже нельзя выполнить описанным ранее способом за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt; в среднем. У нас есть выпуклая оболочка, наша прямая пересекает ее, возможно, «отсекая» несколько отрезков выпуклой оболочки в середине (рис. 4 : красная прямая - та, которую мы хотим вставить в наше множество). Более формально : теперь наша новая прямая будет ниже остальных при &amp;lt;tex&amp;gt;x \in [x1; x2]&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;x1, x2 \in R&amp;lt;/tex&amp;gt; - точки пересечения с некоторыми прямыми, причем &amp;lt;tex&amp;gt;x2&amp;lt;/tex&amp;gt; не обязательно равно &amp;lt;tex&amp;gt;+ \infty&amp;lt;/tex&amp;gt;&lt;br /&gt;
[[Файл:picture4convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
Чтобы уметь вставлять прямую в множество будем хранить &amp;lt;math&amp;gt;std::set&amp;lt;/math&amp;gt; (или любой аналог в других языках программирования) пар &amp;lt;tex&amp;gt;(k, st)&amp;lt;/tex&amp;gt; =  &amp;lt;tex&amp;gt;(коэффицент прямой, ее номер в глобальной нумерации)&amp;lt;/tex&amp;gt;. Когда приходит новая прямая, ищем последнюю прямую с меньшим угловым коэффицентом, чем у той прямой, которую мы хотим добавить в множество. Поиск такой прямой занимает &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt;. Начиная с найденной прямой выполняем &amp;quot;старый&amp;quot; алгоритм (удаляем, пока текущая прямая множества бесполезна). И симметричный алгоритм применяем ко всем прямым справа от нашей (удаляем правого соседа нашей прямой, пока она пересекает нас позже, чем своего правого соседа).&lt;br /&gt;
	&lt;br /&gt;
Асимптотика решения составит &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; на каждый из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; запросов «добавить прямую» + &amp;lt;tex&amp;gt;O(n\cdot\log(n))&amp;lt;/tex&amp;gt; суммарно на удаление прямых, т.к. по-прежнему каждая прямая не более одного раза удалится из множества, а каждое удаление из std::set занимает &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; времени. Итого &amp;lt;math&amp;gt;O(n\cdot\log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Альтернативный подход ==&lt;br /&gt;
Другой способ интерпретировать выражение &amp;lt;tex&amp;gt;dp[i] = \min\limits_{j=0...i-1}(c[j] \cdot a[i] + dp[j])&amp;lt;/tex&amp;gt;  заключается в том, что мы будем пытаться свести задачу к стандартной выпуклой оболочке множества точек. Перепишем выражение средующим образом : &amp;lt;tex&amp;gt;dp[j] + a[i] \cdot c[j] = (dp[j], c[j]) \cdot (1, a[i])&amp;lt;/tex&amp;gt;, т.е. запишем как скалярное произведение векторов &amp;lt;tex&amp;gt;v[j] = (dp[j], c[j])&amp;lt;/tex&amp;gt; и &amp;lt;tex &amp;gt;u[i] = (1, a[i])&amp;lt;/tex &amp;gt;. Вектора  &amp;lt;tex &amp;gt;v[j] =  (dp[j], c[j])&amp;lt;/tex&amp;gt; хотелось бы организовать так, чтобы за &amp;lt;tex &amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; находить вектор, максимизирующий выражение &amp;lt;tex&amp;gt;v[j] \cdot u[i]&amp;lt;/tex&amp;gt;. Посмотрим на рис. 5. Заметим интуитивно очевидный факт : красная точка (вектор) &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; не может давать более оптимальное значение &amp;lt;tex&amp;gt;v[j] \cdot u[i]&amp;lt;/tex&amp;gt; одновременно чем обе синие точки. По этой причине нам достаточно оставить выпуклую оболочку векторов &amp;lt;tex&amp;gt;v[j]&amp;lt;/tex&amp;gt;, а ответ на запрос - это поиск &amp;lt;tex&amp;gt;v[j]&amp;lt;/tex&amp;gt;, максимизирующего проекцию на &amp;lt;tex&amp;gt;u[i]&amp;lt;/tex&amp;gt;. Это задача поиска ближайшей точки выпуклого многоугольника (составленного из точек выпуклой оболочки) к заданной прямой (из &amp;lt;tex&amp;gt;(0, 0)&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;(1, a[i])&amp;lt;/tex&amp;gt;).  Ее можно решить за &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; двумя бинарными или одним тернарным поиском&lt;br /&gt;
Асимптотика алгоритма по-прежнему составит &amp;lt;tex&amp;gt;O(n \cdot \log(n))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Файл:picture5convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
Докажем то, что описанный выше алгоритм корректен. Для этого достаточно показать, что если имеются &amp;lt;math&amp;gt;3&amp;lt;/math&amp;gt; вектора &amp;lt;math&amp;gt;a, b, c&amp;lt;/math&amp;gt;, расположенные как на рис. 5, т.е. точка &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; не лежит на выпуклой оболочке векторов &amp;lt;tex&amp;gt;0, a, b, c &amp;lt;/tex&amp;gt; : &amp;lt;tex&amp;gt; \Leftrightarrow |[a-b, b-c]| &amp;lt; 0 &amp;lt;/tex&amp;gt;, то либо  &amp;lt;tex&amp;gt;(a, u[i])&amp;lt;/tex&amp;gt; оптимальнее, чем &amp;lt;tex&amp;gt;(b, u[i])&amp;lt;/tex&amp;gt;,  либо &amp;lt;tex&amp;gt;(c, u[i])&amp;lt;/tex&amp;gt; оптимальнее, чем &amp;lt;tex&amp;gt;(b, u[i])&amp;lt;/tex&amp;gt;.&lt;br /&gt;
{{Теорема&lt;br /&gt;
|id=th12392. &lt;br /&gt;
|statement=Если есть &amp;lt;tex&amp;gt;3&amp;lt;/tex&amp;gt; вектора &amp;lt;tex&amp;gt;a, b, c&amp;lt;/tex&amp;gt;, таких что &amp;lt;tex&amp;gt;|[a-b, b-c]| &amp;lt; 0&amp;lt;/tex&amp;gt; то либо &amp;lt;math&amp;gt;(a, u) &amp;lt; (b, u)&amp;lt;/math&amp;gt;, либо &amp;lt;math&amp;gt;(c, u) &amp;lt; (b, u)&amp;lt;/math&amp;gt;, где вектор &amp;lt;math&amp;gt;u = (1; k)&amp;lt;/math&amp;gt;.&lt;br /&gt;
|proof=&amp;lt;tex&amp;gt;|[a-b, b-c]| &amp;lt; 0 \Leftrightarrow (a_{x} - b_{x})\cdot(b_{y} - c_{y}) &amp;lt; (a_{y} - b_{y}) \cdot (b_{x} - c_{x})&amp;lt;/tex&amp;gt; (*). Предположим (от противного), что &amp;lt;tex&amp;gt;(b, u) &amp;lt; (a, u) \Leftrightarrow b_{x}  + k \cdot b_{y} &amp;lt; c_{x} + k \cdot c_{y} \Leftrightarrow (b_{x} - c_{x}) &amp;lt; k \cdot (c_{y} - b_{y})&amp;lt;/tex&amp;gt; и  &amp;lt;tex&amp;gt;(b, u) &amp;lt; (c, u) \Leftrightarrow b_{x}  + k \cdot b_{y} &amp;lt; a_{x} + k \cdot a_{y} \Leftrightarrow (a_{x} - b_{x}) &amp;gt; k \cdot (b_{y} - a_{y})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Подставим эти неравенства в (*). Получим цепочку неравенств : &amp;lt;tex&amp;gt;k \cdot (a_{y} - b_{y})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (c_{y} - b_{y}) = k&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (b_{y} - a_{y}) \cdot &amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt;(b_{y} - c_{y})&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt; &amp;lt; (a_{x} - b_{x})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (b_{y} - c_{y})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; &amp;lt; (a_{y} - b_{y}) \cdot &amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt;(b_{x} - c_{x})&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;&amp;lt; k \cdot (a_{y} - b_{y})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (c_{y} - b_{y})&amp;lt;/tex&amp;gt;. Получили противоречие : &amp;lt;tex&amp;gt;k \cdot (a_{y} - b_{y}) \cdot (c_{y} - b_{y}) &amp;lt; k \cdot (a_{y} - b_{y}) \cdot (c_{y} - b_{y})&amp;lt;/tex&amp;gt;. Значит предположение неверно, чтд.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Из доказанной теоремы и следует корректность алгоритма.&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
1) http://neerc.ifmo.ru/wiki/index.php?title=Статические_выпуклые_оболочки:_Джарвис,_Грэхем,_Эндрю,_Чен,_QuickHull&lt;br /&gt;
&lt;br /&gt;
2) http://neerc.ifmo.ru/wiki/index.php?title=Динамическое_программирование&lt;br /&gt;
&lt;br /&gt;
[[Категория:Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория: Динамическое программирование]]&lt;br /&gt;
[[Категория: Способы оптимизации методов динамического программирования]]&lt;/div&gt;</summary>
		<author><name>92.255.113.52</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=Convex_hull_trick&amp;diff=60028</id>
		<title>Convex hull trick</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=Convex_hull_trick&amp;diff=60028"/>
				<updated>2017-01-18T17:14:31Z</updated>
		
		<summary type="html">&lt;p&gt;92.255.113.52: /* Альтернативный подход */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Convex hull trick -- один из методов оптимизации динамического программирования [[http://neerc.ifmo.ru/wiki/index.php?title=Динамическое_программирование]], использующий идею выпуклой оболочки. Позволяет улучшить ассимптотику решения некоторых задачь, решемых методом динамического программирования с &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt; до &amp;lt;tex&amp;gt;O(n\cdot\log(n))&amp;lt;/tex&amp;gt;. Техника впервые появилась в 1995 году (задачу на нее предложили в USACO -- национальной олимпиаде США по программированию). Массовую известность получила после IOI (международной олимпиады по программированию для школьников) 2002.&lt;br /&gt;
&lt;br /&gt;
==Пример задачи, решаемой методом convex hull trick==&lt;br /&gt;
Рассмотрим задачу на ДП:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; деревьев с высотами &amp;lt;tex&amp;gt;a_1, a_2, \dots, a_n&amp;lt;/tex&amp;gt; (в метрах). Требуется спилить их все, потратив минимальное количество монет на заправку&lt;br /&gt;
бензопилы. Но пила устроена так, что она может спиливать только по 1 метру от дерева, к которому ее применили. Также после&lt;br /&gt;
срубленного метра (любого дерева) пилу нужно заправлять, платя за  бензин определенной кол-во монет. Причем стоимость &lt;br /&gt;
бензина зависит от срубленных (полностью) деревьев. Если сейчас максимальный индекс срубленного дерева равен &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, то цена заправки &lt;br /&gt;
равна &amp;lt;tex&amp;gt;c_i&amp;lt;/tex&amp;gt;.  Изначально пила заправлена.&lt;br /&gt;
Также известны следующие ограничения : &amp;lt;tex&amp;gt;c_n = 0, a_1 = 1, a_i&amp;lt;/tex&amp;gt; возрастают, &amp;lt;tex&amp;gt;c_i&amp;lt;/tex&amp;gt; убывают. Изначально пила заправлена.&lt;br /&gt;
(убывание и возрастание нестрогие)&lt;br /&gt;
}}&lt;br /&gt;
(Задача H с Санкт-Петербургских сборов к РОИ 2016[http://neerc.ifmo.ru/school/camp-2016/problems/20160318a.pdf])&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
&amp;lt;includeonly&amp;gt;{{#if: {{{neat|}}}|&lt;br /&gt;
&amp;lt;div style=&amp;quot;background-color: #fcfcfc; float:left;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;background-color: #ddd;&amp;quot;&amp;gt;'''Задача:'''&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;border:1px dashed #2f6fab; padding: 8px; font-style: italic;&amp;quot;&amp;gt;{{{definition}}}&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;|&lt;br /&gt;
&amp;lt;table border=&amp;quot;0&amp;quot; width=&amp;quot;100%&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td style=&amp;quot;background-color: #ddd&amp;quot;&amp;gt;'''Задача:'''&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td style=&amp;quot;border:1px dashed #2f6fab; padding: 8px; background-color: #fcfcfc; font-style: italic;&amp;quot;&amp;gt;{{{definition}}}&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;}}&lt;br /&gt;
&amp;lt;/includeonly&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Наивное решение==&lt;br /&gt;
Сначала заметим важный факт : т.к. &amp;lt;tex&amp;gt;c[i]&amp;lt;/tex&amp;gt; убывают (нестрого) и &amp;lt;tex&amp;gt;c[n] = 0&amp;lt;/tex&amp;gt;, то все &amp;lt;tex&amp;gt;c[i]&amp;lt;/tex&amp;gt; неотрицательны.&lt;br /&gt;
Понятно, что нужно затратив минимальную стоимость срубить последнее (&amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;-е) дерево, т.к. после него все деревья можно будет рубить бесплатно (т.к. &amp;lt;tex&amp;gt;c[n] = 0&amp;lt;/tex&amp;gt;). Посчитаем следующую динамику : &amp;lt;tex&amp;gt;dp[i]&amp;lt;/tex&amp;gt; - минимальная стоимость, заплатив которую можно добиться того, что дерево номер &amp;lt;tex&amp;gt;i.&amp;lt;/tex&amp;gt; будет срублено.&lt;br /&gt;
База динамики : &amp;lt;tex&amp;gt;dp[1] = 0&amp;lt;/tex&amp;gt;, т.к. изначально пила заправлена и высота первого дерева равна 1, по условию задачи.&lt;br /&gt;
Переход динамики :  понятно, что выгодно рубить сначала более дорогие и низкие деревья, а потом более высокие и дешевые (док-во этого факта оставляется читателям как несложное упражнение, т.к. эта идея относится скорее к теме жадных алгоритмнов, чем к теме данной статьи). Поэтому перед  &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-м деревом мы обязательно срубили какое-то &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-е, причем &amp;lt;tex&amp;gt;j \leqslant i - 1&amp;lt;/tex&amp;gt;. Поэтому чтобы найти &amp;lt;tex&amp;gt;dp[i]&amp;lt;/tex&amp;gt; нужно перебрать все &amp;lt;tex&amp;gt;1 \leqslant j \leqslant i - 1&amp;lt;/tex&amp;gt; и попытаться использовать ответ для дерева намер &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;. Итак, пусть перед &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-м деревом мы полностью срубили &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-е, причем высота &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева составляет &amp;lt;tex&amp;gt;a[i]&amp;lt;/tex&amp;gt;, а т.к. последнее дерево, которое мы срубили имеет индекс &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, то стоимость каждого метра &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева составит &amp;lt;tex&amp;gt;c[j]&amp;lt;/tex&amp;gt;.  Поэтому на сруб &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева мы потратим &amp;lt;tex&amp;gt;a[i] \cdot c[j]&amp;lt;/tex&amp;gt; монет. Также не стоит забывать, ситуацию, когда  &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-е дерево полностью срублено, мы получили не бесплатно, а за &amp;lt;tex&amp;gt;dp[j]&amp;lt;/tex&amp;gt; монет.&lt;br /&gt;
Итогвая формула пересчета : &amp;lt;tex&amp;gt;dp[i] = \min\limits_{j=1...i-1} (dp[j] + a[i] \cdot c[j])&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Посмотрим на код выше описанного решения:&lt;br /&gt;
 '''int''' &amp;lt;tex&amp;gt;\mathtt{simpleDP}&amp;lt;/tex&amp;gt;('''int''' a[n], '''int''' c[n])&lt;br /&gt;
    dp[1] = 0&lt;br /&gt;
    dp[2] = dp[3] = ... = dp[n] = &amp;lt;tex&amp;gt;\infty&amp;lt;/tex&amp;gt;&lt;br /&gt;
    '''for'''  i = 1..n-1  &lt;br /&gt;
       dp[i] = &amp;lt;tex&amp;gt;+\infty&amp;lt;/tex&amp;gt;&lt;br /&gt;
       '''for''' j = 0..i-1 &lt;br /&gt;
           '''if''' (dp[j] + a[i] &amp;lt;tex&amp;gt;\cdot&amp;lt;/tex&amp;gt; c[j] &amp;lt; dp[i])&lt;br /&gt;
           dp[i] = dp[j] + a[i] &amp;lt;tex&amp;gt;\cdot&amp;lt;/tex&amp;gt; c[j]&lt;br /&gt;
 '''return''' dp[n]&lt;br /&gt;
Нетрудно видеть, что такая динамика работает за &amp;lt;tex&amp;gt;O(n^2)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Ключевая идея оптимизации==&lt;br /&gt;
Для начала сделаем замену обозначений. Давайте обозначим &amp;lt;tex&amp;gt;dp[j]&amp;lt;/tex&amp;gt; за &amp;lt;tex&amp;gt;b[j]&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;a[i]&amp;lt;/tex&amp;gt; за &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt;, а &amp;lt;tex&amp;gt;c[j]&amp;lt;/tex&amp;gt; за &amp;lt;tex&amp;gt;k[j]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь формула приняла вид &amp;lt;tex&amp;gt;dp[i] = \min\limits_{j=0...i-1}(k[j] \cdot x[i] + b[j])&amp;lt;/tex&amp;gt;. Выражение &amp;lt;tex&amp;gt;k[j] \cdot x + b[j]&amp;lt;/tex&amp;gt; - это в точности уравнение прямой вида &amp;lt;tex&amp;gt;y = kx + b&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сопоставим каждому &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, обработанному ранее, прямую &amp;lt;tex&amp;gt;y[j](x) = k[j] \cdot x + b[j]&amp;lt;/tex&amp;gt;. Из условия «&amp;lt;tex&amp;gt;c[i]&amp;lt;/tex&amp;gt; убывают &amp;lt;tex&amp;gt;\Leftrightarrow  k[j]&amp;lt;/tex&amp;gt; уменьшаются с номером &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;» следует то, что прямые, полученные ранее отсортированы в порядке убывания углового коэффициент. Давайте нарисуем несколько таких прямых :&lt;br /&gt;
&lt;br /&gt;
[[Файл:picture1convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
Выделим множество точек &amp;lt;tex&amp;gt;(x0, y0)&amp;lt;/tex&amp;gt; , таких что все они принадлежат одной из прямых и при этом нету ни одной прямой &amp;lt;tex&amp;gt;y’(x)&amp;lt;/tex&amp;gt;, такой что &amp;lt;tex&amp;gt;y’(x0) &amp;lt; y0&amp;lt;/tex&amp;gt;. Иными словами возьмем «выпуклую (вверх) оболочку» нашего множества прямых (её еще называют нижней ошибающей множества прямых на плоскости). Назовем ее «&amp;lt;tex&amp;gt;y = convex(x)&amp;lt;/tex&amp;gt;». Видно, что множество точек &amp;lt;math&amp;gt;(x, convex(x))&amp;lt;/math&amp;gt; представляет собой выпуклую вверх функцию.&lt;br /&gt;
&lt;br /&gt;
==Цель нижней огибающей множества прямых==&lt;br /&gt;
Пусть мы считаем динамику для &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева. Его задает &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt;. Итак, нам нужно для данного &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt; найти &amp;lt;tex&amp;gt;\min\limits_{j=0..i-1}(k[j] \cdot x[i] + b[i]) = \min\limits_{j=0..i-1}(y[j](x[i]))&amp;lt;/tex&amp;gt;. Это выражение есть &amp;lt;math&amp;gt;convex(x[i])&amp;lt;/math&amp;gt;. Из монотонности угловых коэффицентов отрезков, задающих выпуклую оболочку, и их расположения по координаты x следует то, что отрезок, который пересекает прямую &amp;lt;tex&amp;gt;x = x[i]&amp;lt;/tex&amp;gt;, можно найти бинарным поиском. Это потребует &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; времени на поиск такого &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;dp[i] = k[j] \cdot x[i] + b[j]&amp;lt;/tex&amp;gt;. Теперь осталось научиться поддерживать множество прямых и быстро добавлять &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-ю прямую после того, как мы посчитали &amp;lt;tex&amp;gt;b[i] = dp[i]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Воспользуемся идеей алгоритма построения выпуклой оболочки множества точек. Заведем 2 стека &amp;lt;tex&amp;gt;k[]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;b[]&amp;lt;/tex&amp;gt;, которые задают прямые в отсортированном порядке их угловыми коэффицентами и свободными членами. Рассмотрим ситуацию, когда мы хотим добавить новую (&amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-тую) прямую в множество. Пусть сейчас в множестве лежит &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt; прямых (нумерация с 1). Пусть &amp;lt;tex&amp;gt;(xL, yL)&amp;lt;/tex&amp;gt; - точка пересечения &amp;lt;tex&amp;gt;sz - 1&amp;lt;/tex&amp;gt;-й прямой множества и &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-й, а &amp;lt;tex&amp;gt;(xR, yR)&amp;lt;/tex&amp;gt; - точка пересечения новой прямой, которую мы хотим добавить в конец множества и &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-й. Нас будут интересовать только их &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;-овые координаты &amp;lt;tex&amp;gt;xL&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;xR&amp;lt;/tex&amp;gt;, соответственно. Если оказалось, что новая прямая пересекает &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-ю прямую выпуклой оболочки позже, чем &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-я &amp;lt;tex&amp;gt;sz - 1&amp;lt;/tex&amp;gt;-ю, т.е. &amp;lt;tex&amp;gt;(xL \geqslant xR)&amp;lt;/tex&amp;gt;, то &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-ю удалим из нашего множества, иначе - остановимся. Так будем делать, пока либо кол-во прямых в стеке не станет равным 2, либо &amp;lt;tex&amp;gt;xL&amp;lt;/tex&amp;gt; не станет меньше &amp;lt;tex&amp;gt;xR.&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Асимптотика : аналогично обычному алгоритму построения выпуклой оболочки, каждая прямая ровно &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; раз добавится в стек и максимум &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; раз удалится. Значит время работы перестройки выпуклой оболочки займет &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; суммарно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:picture2convexhull.png]]&lt;br /&gt;
[[Файл:picture3convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
{{Теорема&lt;br /&gt;
|id=th1239. &lt;br /&gt;
|statement=Алгоритм построения нижней огибающей множества прямых корректен.&lt;br /&gt;
|proof=Достаточно показать, что последнюю прямую нужно удалить из множества т.и т.т., когда она наша новая прямая пересекает ее в точке с координатой по оси X, меньшей, чем последняя - предпоследнюю.&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;Y(x) = Kx + B&amp;lt;/tex&amp;gt; - уравнение новой прямой,  &amp;lt;tex&amp;gt;y[i](x) = K[i]x + B[i]&amp;lt;/tex&amp;gt; - уравнения прямых множества. Тогда т.к. &amp;lt;tex&amp;gt;K &amp;lt; K[sz]&amp;lt;/tex&amp;gt;, то при &amp;lt;tex&amp;gt;x \in [- \infty; xR]  : y[sz](x) &amp;lt;= Y(x)&amp;lt;/tex&amp;gt;, а т.к. &amp;lt;tex&amp;gt; K[sz] &amp;lt; K[sz - 1]&amp;lt;/tex&amp;gt;, то при &amp;lt;tex&amp;gt;x \in [xL; + \infty]  : y[sz - 1](x) \geqslant y[sz](x)&amp;lt;/tex&amp;gt;. Если &amp;lt;tex&amp;gt;xL &amp;lt; xR&amp;lt;/tex&amp;gt;, то при &amp;lt;tex&amp;gt;x \in [xL; xR]  : y[sz - 1] \geqslant y[sz](x) и Y(x) \geqslant y[sz](x)&amp;lt;/tex&amp;gt;, т.е. на отрезке &amp;lt;tex&amp;gt;[xL; xR]&amp;lt;/tex&amp;gt; прямая номер sz лежит ниже остальных и её нужно оставить в множестве. Если же &amp;lt;tex&amp;gt;xL &amp;gt; xR&amp;lt;/tex&amp;gt;, то она ниже всех на отрезке &amp;lt;tex&amp;gt;[xL; xR] = \varnothing &amp;lt;/tex&amp;gt;, т.е. её можно удалить из множества&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==Детали реализации:==&lt;br /&gt;
Будем хранить 2 массива : &amp;lt;tex&amp;gt;front[]&amp;lt;/tex&amp;gt; - &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;-координаты, начиная с которых прямые совпадают с выпуклой оболочкой (т.е. i-я прямая совпадает с выпуклой оболочкой текущего множества прямых при &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;[front[i]; front[i + 1])&amp;lt;/tex&amp;gt; ) и &amp;lt;tex&amp;gt;st[]&amp;lt;/tex&amp;gt; - номера деревьев, соответствующих прямым (т.е. &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-я прямая множества, где &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;[1; sz]&amp;lt;/tex&amp;gt; соответствует дереву номер &amp;lt;tex&amp;gt;sz[i]&amp;lt;/tex&amp;gt;). Также воспользуемся тем, что &amp;lt;tex&amp;gt;x[i] = a[i]&amp;lt;/tex&amp;gt; возрастают (по условию задачи), а значит мы можем искать первое такое &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;x[i] \geqslant front[j]&amp;lt;/tex&amp;gt; не бинарным поиском, а методом двух указателей за &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; операций суммарно. Также массив front[] можно хранить в целых числах, округляя х-координаты в сторону лежащих правее по оси x до ближайшего целого (*), т.к. на самом деле мы, считая динамику, подставляем в уравнения прямых только целые &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt;, а значит если &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt;-я прямая пересекается с &amp;lt;tex&amp;gt;k+1&amp;lt;/tex&amp;gt;-й в точке &amp;lt;tex&amp;gt;z +&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\alpha&amp;lt;/tex&amp;gt; (z-целое,  &amp;lt;tex&amp;gt;\alpha&amp;lt;/tex&amp;gt;  &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;[0;1)&amp;lt;/tex&amp;gt;), то мы будем подставлять в их уравнения &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt;  или &amp;lt;tex&amp;gt;z + 1&amp;lt;/tex&amp;gt;. Поэтому можно считать, что новая прямая начинает совпадать с выпуклой оболочкой, начиная с &amp;lt;tex&amp;gt;x = z+1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Реализация==&lt;br /&gt;
  '''int''' &amp;lt;tex&amp;gt;\mathtt{ConvexHullTrick}&amp;lt;/tex&amp;gt;('''int''' a[n], '''int''' c[n])&lt;br /&gt;
     st[1] = 1&lt;br /&gt;
     from[1] = -&amp;lt;tex&amp;gt;\infty&amp;lt;/tex&amp;gt;&amp;lt;font color=green&amp;gt;// первая прямая покрывает все x-ы, начиная с -∞ &amp;lt;/font&amp;gt;&lt;br /&gt;
     sz = 1 &amp;lt;font color=green&amp;gt;// текущий размер выпуклой оболочки &amp;lt;/font&amp;gt;&lt;br /&gt;
     pos = 1 &amp;lt;font color=green&amp;gt;// текущая позиция первого такого j, что x[i] \geqslant front[st[j]] &amp;lt;/font &amp;gt;&lt;br /&gt;
     '''for'''  i = 2..n  &lt;br /&gt;
          '''while''' (front[pos] &amp;lt; x[i]) &amp;lt;font color=green&amp;gt;// метод 1 указателя (ищем первое pos, такое что x[i] покрывается &amp;quot;областью действия&amp;quot; st[pos]-той прямой &amp;lt;/font &amp;gt;&lt;br /&gt;
               pos = pos + 1&lt;br /&gt;
          j = st[pos]&lt;br /&gt;
          dp[i] = K[j]&amp;lt;math&amp;gt;\cdot&amp;lt;/math&amp;gt;a[i] + B[j] &lt;br /&gt;
          '''if''' (i &amp;lt; n)   &amp;lt;font color=green&amp;gt;// если у нас добавляется НЕ последняя прямая, то придется пересчитать выпуклую оболочку &amp;lt;/font &amp;gt;&lt;br /&gt;
                K[i] = c[i]  &amp;lt;font color=green&amp;gt;// наши переобозначения переменных &amp;lt;/font &amp;gt;&lt;br /&gt;
                B[i] = dp[i] &amp;lt;font color=green&amp;gt;// наши переобозначения переменных &amp;lt;/font &amp;gt;&lt;br /&gt;
                x = -&amp;lt;tex&amp;gt;\infty&amp;lt;/tex&amp;gt; &lt;br /&gt;
                '''while''' ''true'' &lt;br /&gt;
                      j = st[sz]&lt;br /&gt;
                      x = divide(B[j] - B[i], K[i] - K[j]) &amp;lt;font color=green&amp;gt;// x-координата пересечения с последней прямой оболочки, округленное в нужную сторону (*) &amp;lt;/font &amp;gt;&lt;br /&gt;
                      '''if''' (x &amp;gt; from[sz]) '''break'''  &amp;lt;font color=green&amp;gt;// перестаем удалять последнюю прямую из множества, если новая прямая пересекает ее позже, чем начинается ее &amp;quot;область действия&amp;quot; &amp;lt;/font &amp;gt;&lt;br /&gt;
                      sz = sz - 1&amp;lt;font color=green&amp;gt;// удаляем последнюю прямую, если она лишняя &amp;lt;/font &amp;gt;&lt;br /&gt;
                 st[sz + 1] = i  &lt;br /&gt;
                 from[sz + 1] = x &amp;lt;font color=green&amp;gt;// добавили новую прямую &amp;lt;/font &amp;gt;&lt;br /&gt;
                 sz = sz + 1&lt;br /&gt;
  '''return''' dp[n] &lt;br /&gt;
Здесь функция &amp;lt;tex&amp;gt;\mathtt{divide}&amp;lt;/tex&amp;gt;(a, b) возвращает нужное(*) округление a / b. Приведем её код :&lt;br /&gt;
 '''int''' &amp;lt;tex&amp;gt;\mathtt{divide}&amp;lt;/tex&amp;gt;('''int''' a, '''int''' b)&lt;br /&gt;
        delta = 0&lt;br /&gt;
        '''if''' (a '''mod''' b ≠ 0) delta = 1&lt;br /&gt;
        '''if''' ((a &amp;gt; 0 '''and''' b &amp;gt; 0) '''or''' (a &amp;lt; 0 '''and''' b &amp;lt; 0)) '''return''' [a / b] + delta&lt;br /&gt;
 '''return''' -[|a| / |b|]&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
Такая реализация будет работать за O(n).&lt;br /&gt;
&lt;br /&gt;
==Динамический convex hull trick==&lt;br /&gt;
	Заметим, что условия на прямые, что &amp;lt;tex&amp;gt;k[i]&amp;lt;/tex&amp;gt; возрастает/убывает и &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt; убывает/возрастает выглядят достаточно редкими для большинства задач. Пусть в задаче таких ограничений нет. Первый способ борьбы с этой проблемой - отсортировать входные данные нужным образом, не испортив свойств задачи (пример : задача G c Санкт-Петербургских сборов к РОИ 2016[http://neerc.ifmo.ru/school/camp-2016/problems/20160318a.pdf]).&lt;br /&gt;
&lt;br /&gt;
	Но рассмотрим общий случай. По-прежнему у нас есть выпуклая оболочка прямых, имея которую мы за &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; можем найти   &amp;lt;tex&amp;gt;dp[i]&amp;lt;/tex&amp;gt;, но теперь вставку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-й прямой в оболочку уже нельзя выполнить описанным ранее способом за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt; в среднем. У нас есть выпуклая оболочка, наша прямая пересекает ее, возможно, «отсекая» несколько отрезков выпуклой оболочки в середине (рис. 4 : красная прямая - та, которую мы хотим вставить в наше множество). Более формально : теперь наша новая прямая будет ниже остальных при &amp;lt;tex&amp;gt;x \in [x1; x2]&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;x1, x2 \in R&amp;lt;/tex&amp;gt; - точки пересечения с некоторыми прямыми, причем &amp;lt;tex&amp;gt;x2&amp;lt;/tex&amp;gt; не обязательно равно &amp;lt;tex&amp;gt;+ \infty&amp;lt;/tex&amp;gt;&lt;br /&gt;
[[Файл:picture4convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
Чтобы уметь вставлять прямую в множество будем хранить &amp;lt;math&amp;gt;std::set&amp;lt;/math&amp;gt; (или любой аналог в других языках программирования) пар &amp;lt;tex&amp;gt;(k, st)&amp;lt;/tex&amp;gt; =  &amp;lt;tex&amp;gt;(коэффицент прямой, ее номер в глобальной нумерации)&amp;lt;/tex&amp;gt;. Когда приходит новая прямая, ищем последнюю прямую с меньшим угловым коэффицентом, чем у той прямой, которую мы хотим добавить в множество. Поиск такой прямой занимает &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt;. Начиная с найденной прямой выполняем &amp;quot;старый&amp;quot; алгоритм (удаляем, пока текущая прямая множества бесполезна). И симметричный алгоритм применяем ко всем прямым справа от нашей (удаляем правого соседа нашей прямой, пока она пересекает нас позже, чем своего правого соседа).&lt;br /&gt;
	&lt;br /&gt;
Асимптотика решения составит &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; на каждый из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; запросов «добавить прямую» + &amp;lt;tex&amp;gt;O(n\cdot\log(n))&amp;lt;/tex&amp;gt; суммарно на удаление прямых, т.к. по-прежнему каждая прямая не более одного раза удалится из множества, а каждое удаление из std::set занимает &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; времени. Итого &amp;lt;math&amp;gt;O(n\cdot\log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Альтернативный подход ==&lt;br /&gt;
Другой способ интерпретировать выражение &amp;lt;tex&amp;gt;dp[i] = \min\limits_{j=0...i-1}(c[j] \cdot a[i] + dp[j])&amp;lt;/tex&amp;gt;  заключается в том, что мы будем пытаться свести задачу к стандартной выпуклой оболочке множества точек. Перепишем выражение средующим образом : &amp;lt;tex&amp;gt;dp[j] + a[i] \cdot c[j] = (dp[j], c[j]) \cdot (1, a[i])&amp;lt;/tex&amp;gt;, т.е. запишем как скалярное произведение векторов &amp;lt;tex&amp;gt;v[j] = (dp[j], c[j])&amp;lt;/tex&amp;gt; и &amp;lt;tex &amp;gt;u[i] = (1, a[i])&amp;lt;/tex &amp;gt;. Вектора  &amp;lt;tex &amp;gt;v[j] =  (dp[j], c[j])&amp;lt;/tex&amp;gt; хотелось бы организовать так, чтобы за &amp;lt;tex &amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; находить вектор, максимизирующий выражение &amp;lt;tex&amp;gt;v[j] \cdot u[i]&amp;lt;/tex&amp;gt;. Посмотрим на рис. 5. Заметим интуитивно очевидный факт : красная точка (вектор) &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; не может давать более оптимальное значение &amp;lt;tex&amp;gt;v[j] \cdot u[i]&amp;lt;/tex&amp;gt; одновременно чем обе синие точки. По этой причине нам достаточно оставить выпуклую оболочку векторов &amp;lt;tex&amp;gt;v[j]&amp;lt;/tex&amp;gt;, а ответ на запрос - это поиск &amp;lt;tex&amp;gt;v[j]&amp;lt;/tex&amp;gt;, максимизирующего проекцию на &amp;lt;tex&amp;gt;u[i]&amp;lt;/tex&amp;gt;. Это задача поиска ближайшей точки выпуклого многоугольника (составленного из точек выпуклой оболочки) к заданной прямой (из &amp;lt;tex&amp;gt;(0, 0)&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;(1, a[i])&amp;lt;/tex&amp;gt;).  Ее можно решить за &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; двумя бинарными или одним тернарным поиском&lt;br /&gt;
Асимптотика алгоритма по-прежнему составит &amp;lt;tex&amp;gt;O(n \cdot \log(n))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Файл:picture5convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
Докажем то, что описанный выше алгоритм корректен. Для этого достаточно показать, что если имеются &amp;lt;math&amp;gt;3&amp;lt;/math&amp;gt; вектора &amp;lt;math&amp;gt;a, b, c&amp;lt;/math&amp;gt;, расположенные как на рис. 5, т.е. точка &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; не лежит на выпуклой оболочке векторов &amp;lt;tex&amp;gt;0, a, b, c &amp;lt;/tex&amp;gt; : &amp;lt;tex&amp;gt; \Leftrightarrow |[a-b, b-c]| &amp;lt; 0 &amp;lt;/tex&amp;gt;, то либо  &amp;lt;tex&amp;gt;(a, u[i])&amp;lt;/tex&amp;gt; оптимальнее, чем &amp;lt;tex&amp;gt;(b, u[i])&amp;lt;/tex&amp;gt;,  либо &amp;lt;tex&amp;gt;(c, u[i])&amp;lt;/tex&amp;gt; оптимальнее, чем &amp;lt;tex&amp;gt;(b, u[i])&amp;lt;/tex&amp;gt;&lt;br /&gt;
{{Теорема&lt;br /&gt;
|id=th12392. &lt;br /&gt;
|statement=Если есть &amp;lt;tex&amp;gt;3&amp;lt;/tex&amp;gt; вектора &amp;lt;tex&amp;gt;a, b, c&amp;lt;/tex&amp;gt;, таких что &amp;lt;tex&amp;gt;|[a-b, b-c]| &amp;lt; 0&amp;lt;/tex&amp;gt; то либо &amp;lt;math&amp;gt;(a, u) &amp;lt; (b, u)&amp;lt;/math&amp;gt;, либо &amp;lt;math&amp;gt;(c, u) &amp;lt; (b, u)&amp;lt;/math&amp;gt;, где вектор &amp;lt;math&amp;gt;u = (1; k)&amp;lt;/math&amp;gt;.&lt;br /&gt;
|proof=&amp;lt;tex&amp;gt;|[a-b, b-c]| &amp;lt; 0 \Leftrightarrow (a_{x} - b_{x})\cdot(b_{y} - c_{y}) &amp;lt; (a_{y} - b_{y}) \cdot (b_{x} - c_{x})&amp;lt;/tex&amp;gt; (*). Предположим (от противного), что &amp;lt;tex&amp;gt;(b, u) &amp;lt; (a, u) \Leftrightarrow b_{x}  + k \cdot b_{y} &amp;lt; c_{x} + k \cdot c_{y} \Leftrightarrow (b_{x} - c_{x}) &amp;lt; k \cdot (c_{y} - b_{y})&amp;lt;/tex&amp;gt; и  &amp;lt;tex&amp;gt;(b, u) &amp;lt; (c, u) \Leftrightarrow b_{x}  + k \cdot b_{y} &amp;lt; a_{x} + k \cdot a_{y} \Leftrightarrow (a_{x} - b_{x}) &amp;gt; k \cdot (b_{y} - a_{y})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Подставим эти неравенства в (*). Получим цепочку неравенств : &amp;lt;tex&amp;gt;k \cdot (a_{y} - b_{y})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (c_{y} - b_{y}) = k&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (b_{y} - a_{y}) \cdot &amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt;(b_{y} - c_{y})&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt; &amp;lt; (a_{x} - b_{x})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (b_{y} - c_{y})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; &amp;lt; (a_{y} - b_{y}) \cdot &amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt;(b_{x} - c_{x})&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;&amp;lt; k \cdot (a_{y} - b_{y})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (c_{y} - b_{y})&amp;lt;/tex&amp;gt;. Получили противоречие : &amp;lt;tex&amp;gt;k \cdot (a_{y} - b_{y}) \cdot (c_{y} - b_{y}) &amp;lt; k \cdot (a_{y} - b_{y}) \cdot (c_{y} - b_{y})&amp;lt;/tex&amp;gt;. Значит предположение неверно, чтд.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Из доказанной теоремы и следует корректность алгоритма.&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
1) http://neerc.ifmo.ru/wiki/index.php?title=Статические_выпуклые_оболочки:_Джарвис,_Грэхем,_Эндрю,_Чен,_QuickHull&lt;br /&gt;
&lt;br /&gt;
2) http://neerc.ifmo.ru/wiki/index.php?title=Динамическое_программирование&lt;br /&gt;
&lt;br /&gt;
[[Категория:Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория: Динамическое программирование]]&lt;br /&gt;
[[Категория: Способы оптимизации методов динамического программирования]]&lt;/div&gt;</summary>
		<author><name>92.255.113.52</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=Convex_hull_trick&amp;diff=60027</id>
		<title>Convex hull trick</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=Convex_hull_trick&amp;diff=60027"/>
				<updated>2017-01-18T17:13:57Z</updated>
		
		<summary type="html">&lt;p&gt;92.255.113.52: /* Альтернативный подход */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Convex hull trick -- один из методов оптимизации динамического программирования [[http://neerc.ifmo.ru/wiki/index.php?title=Динамическое_программирование]], использующий идею выпуклой оболочки. Позволяет улучшить ассимптотику решения некоторых задачь, решемых методом динамического программирования с &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt; до &amp;lt;tex&amp;gt;O(n\cdot\log(n))&amp;lt;/tex&amp;gt;. Техника впервые появилась в 1995 году (задачу на нее предложили в USACO -- национальной олимпиаде США по программированию). Массовую известность получила после IOI (международной олимпиады по программированию для школьников) 2002.&lt;br /&gt;
&lt;br /&gt;
==Пример задачи, решаемой методом convex hull trick==&lt;br /&gt;
Рассмотрим задачу на ДП:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; деревьев с высотами &amp;lt;tex&amp;gt;a_1, a_2, \dots, a_n&amp;lt;/tex&amp;gt; (в метрах). Требуется спилить их все, потратив минимальное количество монет на заправку&lt;br /&gt;
бензопилы. Но пила устроена так, что она может спиливать только по 1 метру от дерева, к которому ее применили. Также после&lt;br /&gt;
срубленного метра (любого дерева) пилу нужно заправлять, платя за  бензин определенной кол-во монет. Причем стоимость &lt;br /&gt;
бензина зависит от срубленных (полностью) деревьев. Если сейчас максимальный индекс срубленного дерева равен &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, то цена заправки &lt;br /&gt;
равна &amp;lt;tex&amp;gt;c_i&amp;lt;/tex&amp;gt;.  Изначально пила заправлена.&lt;br /&gt;
Также известны следующие ограничения : &amp;lt;tex&amp;gt;c_n = 0, a_1 = 1, a_i&amp;lt;/tex&amp;gt; возрастают, &amp;lt;tex&amp;gt;c_i&amp;lt;/tex&amp;gt; убывают. Изначально пила заправлена.&lt;br /&gt;
(убывание и возрастание нестрогие)&lt;br /&gt;
}}&lt;br /&gt;
(Задача H с Санкт-Петербургских сборов к РОИ 2016[http://neerc.ifmo.ru/school/camp-2016/problems/20160318a.pdf])&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
&amp;lt;includeonly&amp;gt;{{#if: {{{neat|}}}|&lt;br /&gt;
&amp;lt;div style=&amp;quot;background-color: #fcfcfc; float:left;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;background-color: #ddd;&amp;quot;&amp;gt;'''Задача:'''&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;border:1px dashed #2f6fab; padding: 8px; font-style: italic;&amp;quot;&amp;gt;{{{definition}}}&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;|&lt;br /&gt;
&amp;lt;table border=&amp;quot;0&amp;quot; width=&amp;quot;100%&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td style=&amp;quot;background-color: #ddd&amp;quot;&amp;gt;'''Задача:'''&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td style=&amp;quot;border:1px dashed #2f6fab; padding: 8px; background-color: #fcfcfc; font-style: italic;&amp;quot;&amp;gt;{{{definition}}}&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;}}&lt;br /&gt;
&amp;lt;/includeonly&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Наивное решение==&lt;br /&gt;
Сначала заметим важный факт : т.к. &amp;lt;tex&amp;gt;c[i]&amp;lt;/tex&amp;gt; убывают (нестрого) и &amp;lt;tex&amp;gt;c[n] = 0&amp;lt;/tex&amp;gt;, то все &amp;lt;tex&amp;gt;c[i]&amp;lt;/tex&amp;gt; неотрицательны.&lt;br /&gt;
Понятно, что нужно затратив минимальную стоимость срубить последнее (&amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;-е) дерево, т.к. после него все деревья можно будет рубить бесплатно (т.к. &amp;lt;tex&amp;gt;c[n] = 0&amp;lt;/tex&amp;gt;). Посчитаем следующую динамику : &amp;lt;tex&amp;gt;dp[i]&amp;lt;/tex&amp;gt; - минимальная стоимость, заплатив которую можно добиться того, что дерево номер &amp;lt;tex&amp;gt;i.&amp;lt;/tex&amp;gt; будет срублено.&lt;br /&gt;
База динамики : &amp;lt;tex&amp;gt;dp[1] = 0&amp;lt;/tex&amp;gt;, т.к. изначально пила заправлена и высота первого дерева равна 1, по условию задачи.&lt;br /&gt;
Переход динамики :  понятно, что выгодно рубить сначала более дорогие и низкие деревья, а потом более высокие и дешевые (док-во этого факта оставляется читателям как несложное упражнение, т.к. эта идея относится скорее к теме жадных алгоритмнов, чем к теме данной статьи). Поэтому перед  &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-м деревом мы обязательно срубили какое-то &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-е, причем &amp;lt;tex&amp;gt;j \leqslant i - 1&amp;lt;/tex&amp;gt;. Поэтому чтобы найти &amp;lt;tex&amp;gt;dp[i]&amp;lt;/tex&amp;gt; нужно перебрать все &amp;lt;tex&amp;gt;1 \leqslant j \leqslant i - 1&amp;lt;/tex&amp;gt; и попытаться использовать ответ для дерева намер &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;. Итак, пусть перед &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-м деревом мы полностью срубили &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-е, причем высота &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева составляет &amp;lt;tex&amp;gt;a[i]&amp;lt;/tex&amp;gt;, а т.к. последнее дерево, которое мы срубили имеет индекс &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, то стоимость каждого метра &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева составит &amp;lt;tex&amp;gt;c[j]&amp;lt;/tex&amp;gt;.  Поэтому на сруб &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева мы потратим &amp;lt;tex&amp;gt;a[i] \cdot c[j]&amp;lt;/tex&amp;gt; монет. Также не стоит забывать, ситуацию, когда  &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-е дерево полностью срублено, мы получили не бесплатно, а за &amp;lt;tex&amp;gt;dp[j]&amp;lt;/tex&amp;gt; монет.&lt;br /&gt;
Итогвая формула пересчета : &amp;lt;tex&amp;gt;dp[i] = \min\limits_{j=1...i-1} (dp[j] + a[i] \cdot c[j])&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Посмотрим на код выше описанного решения:&lt;br /&gt;
 '''int''' &amp;lt;tex&amp;gt;\mathtt{simpleDP}&amp;lt;/tex&amp;gt;('''int''' a[n], '''int''' c[n])&lt;br /&gt;
    dp[1] = 0&lt;br /&gt;
    dp[2] = dp[3] = ... = dp[n] = &amp;lt;tex&amp;gt;\infty&amp;lt;/tex&amp;gt;&lt;br /&gt;
    '''for'''  i = 1..n-1  &lt;br /&gt;
       dp[i] = &amp;lt;tex&amp;gt;+\infty&amp;lt;/tex&amp;gt;&lt;br /&gt;
       '''for''' j = 0..i-1 &lt;br /&gt;
           '''if''' (dp[j] + a[i] &amp;lt;tex&amp;gt;\cdot&amp;lt;/tex&amp;gt; c[j] &amp;lt; dp[i])&lt;br /&gt;
           dp[i] = dp[j] + a[i] &amp;lt;tex&amp;gt;\cdot&amp;lt;/tex&amp;gt; c[j]&lt;br /&gt;
 '''return''' dp[n]&lt;br /&gt;
Нетрудно видеть, что такая динамика работает за &amp;lt;tex&amp;gt;O(n^2)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Ключевая идея оптимизации==&lt;br /&gt;
Для начала сделаем замену обозначений. Давайте обозначим &amp;lt;tex&amp;gt;dp[j]&amp;lt;/tex&amp;gt; за &amp;lt;tex&amp;gt;b[j]&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;a[i]&amp;lt;/tex&amp;gt; за &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt;, а &amp;lt;tex&amp;gt;c[j]&amp;lt;/tex&amp;gt; за &amp;lt;tex&amp;gt;k[j]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь формула приняла вид &amp;lt;tex&amp;gt;dp[i] = \min\limits_{j=0...i-1}(k[j] \cdot x[i] + b[j])&amp;lt;/tex&amp;gt;. Выражение &amp;lt;tex&amp;gt;k[j] \cdot x + b[j]&amp;lt;/tex&amp;gt; - это в точности уравнение прямой вида &amp;lt;tex&amp;gt;y = kx + b&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сопоставим каждому &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, обработанному ранее, прямую &amp;lt;tex&amp;gt;y[j](x) = k[j] \cdot x + b[j]&amp;lt;/tex&amp;gt;. Из условия «&amp;lt;tex&amp;gt;c[i]&amp;lt;/tex&amp;gt; убывают &amp;lt;tex&amp;gt;\Leftrightarrow  k[j]&amp;lt;/tex&amp;gt; уменьшаются с номером &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;» следует то, что прямые, полученные ранее отсортированы в порядке убывания углового коэффициент. Давайте нарисуем несколько таких прямых :&lt;br /&gt;
&lt;br /&gt;
[[Файл:picture1convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
Выделим множество точек &amp;lt;tex&amp;gt;(x0, y0)&amp;lt;/tex&amp;gt; , таких что все они принадлежат одной из прямых и при этом нету ни одной прямой &amp;lt;tex&amp;gt;y’(x)&amp;lt;/tex&amp;gt;, такой что &amp;lt;tex&amp;gt;y’(x0) &amp;lt; y0&amp;lt;/tex&amp;gt;. Иными словами возьмем «выпуклую (вверх) оболочку» нашего множества прямых (её еще называют нижней ошибающей множества прямых на плоскости). Назовем ее «&amp;lt;tex&amp;gt;y = convex(x)&amp;lt;/tex&amp;gt;». Видно, что множество точек &amp;lt;math&amp;gt;(x, convex(x))&amp;lt;/math&amp;gt; представляет собой выпуклую вверх функцию.&lt;br /&gt;
&lt;br /&gt;
==Цель нижней огибающей множества прямых==&lt;br /&gt;
Пусть мы считаем динамику для &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева. Его задает &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt;. Итак, нам нужно для данного &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt; найти &amp;lt;tex&amp;gt;\min\limits_{j=0..i-1}(k[j] \cdot x[i] + b[i]) = \min\limits_{j=0..i-1}(y[j](x[i]))&amp;lt;/tex&amp;gt;. Это выражение есть &amp;lt;math&amp;gt;convex(x[i])&amp;lt;/math&amp;gt;. Из монотонности угловых коэффицентов отрезков, задающих выпуклую оболочку, и их расположения по координаты x следует то, что отрезок, который пересекает прямую &amp;lt;tex&amp;gt;x = x[i]&amp;lt;/tex&amp;gt;, можно найти бинарным поиском. Это потребует &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; времени на поиск такого &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;dp[i] = k[j] \cdot x[i] + b[j]&amp;lt;/tex&amp;gt;. Теперь осталось научиться поддерживать множество прямых и быстро добавлять &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-ю прямую после того, как мы посчитали &amp;lt;tex&amp;gt;b[i] = dp[i]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Воспользуемся идеей алгоритма построения выпуклой оболочки множества точек. Заведем 2 стека &amp;lt;tex&amp;gt;k[]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;b[]&amp;lt;/tex&amp;gt;, которые задают прямые в отсортированном порядке их угловыми коэффицентами и свободными членами. Рассмотрим ситуацию, когда мы хотим добавить новую (&amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-тую) прямую в множество. Пусть сейчас в множестве лежит &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt; прямых (нумерация с 1). Пусть &amp;lt;tex&amp;gt;(xL, yL)&amp;lt;/tex&amp;gt; - точка пересечения &amp;lt;tex&amp;gt;sz - 1&amp;lt;/tex&amp;gt;-й прямой множества и &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-й, а &amp;lt;tex&amp;gt;(xR, yR)&amp;lt;/tex&amp;gt; - точка пересечения новой прямой, которую мы хотим добавить в конец множества и &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-й. Нас будут интересовать только их &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;-овые координаты &amp;lt;tex&amp;gt;xL&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;xR&amp;lt;/tex&amp;gt;, соответственно. Если оказалось, что новая прямая пересекает &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-ю прямую выпуклой оболочки позже, чем &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-я &amp;lt;tex&amp;gt;sz - 1&amp;lt;/tex&amp;gt;-ю, т.е. &amp;lt;tex&amp;gt;(xL \geqslant xR)&amp;lt;/tex&amp;gt;, то &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-ю удалим из нашего множества, иначе - остановимся. Так будем делать, пока либо кол-во прямых в стеке не станет равным 2, либо &amp;lt;tex&amp;gt;xL&amp;lt;/tex&amp;gt; не станет меньше &amp;lt;tex&amp;gt;xR.&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Асимптотика : аналогично обычному алгоритму построения выпуклой оболочки, каждая прямая ровно &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; раз добавится в стек и максимум &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; раз удалится. Значит время работы перестройки выпуклой оболочки займет &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; суммарно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:picture2convexhull.png]]&lt;br /&gt;
[[Файл:picture3convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
{{Теорема&lt;br /&gt;
|id=th1239. &lt;br /&gt;
|statement=Алгоритм построения нижней огибающей множества прямых корректен.&lt;br /&gt;
|proof=Достаточно показать, что последнюю прямую нужно удалить из множества т.и т.т., когда она наша новая прямая пересекает ее в точке с координатой по оси X, меньшей, чем последняя - предпоследнюю.&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;Y(x) = Kx + B&amp;lt;/tex&amp;gt; - уравнение новой прямой,  &amp;lt;tex&amp;gt;y[i](x) = K[i]x + B[i]&amp;lt;/tex&amp;gt; - уравнения прямых множества. Тогда т.к. &amp;lt;tex&amp;gt;K &amp;lt; K[sz]&amp;lt;/tex&amp;gt;, то при &amp;lt;tex&amp;gt;x \in [- \infty; xR]  : y[sz](x) &amp;lt;= Y(x)&amp;lt;/tex&amp;gt;, а т.к. &amp;lt;tex&amp;gt; K[sz] &amp;lt; K[sz - 1]&amp;lt;/tex&amp;gt;, то при &amp;lt;tex&amp;gt;x \in [xL; + \infty]  : y[sz - 1](x) \geqslant y[sz](x)&amp;lt;/tex&amp;gt;. Если &amp;lt;tex&amp;gt;xL &amp;lt; xR&amp;lt;/tex&amp;gt;, то при &amp;lt;tex&amp;gt;x \in [xL; xR]  : y[sz - 1] \geqslant y[sz](x) и Y(x) \geqslant y[sz](x)&amp;lt;/tex&amp;gt;, т.е. на отрезке &amp;lt;tex&amp;gt;[xL; xR]&amp;lt;/tex&amp;gt; прямая номер sz лежит ниже остальных и её нужно оставить в множестве. Если же &amp;lt;tex&amp;gt;xL &amp;gt; xR&amp;lt;/tex&amp;gt;, то она ниже всех на отрезке &amp;lt;tex&amp;gt;[xL; xR] = \varnothing &amp;lt;/tex&amp;gt;, т.е. её можно удалить из множества&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==Детали реализации:==&lt;br /&gt;
Будем хранить 2 массива : &amp;lt;tex&amp;gt;front[]&amp;lt;/tex&amp;gt; - &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;-координаты, начиная с которых прямые совпадают с выпуклой оболочкой (т.е. i-я прямая совпадает с выпуклой оболочкой текущего множества прямых при &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;[front[i]; front[i + 1])&amp;lt;/tex&amp;gt; ) и &amp;lt;tex&amp;gt;st[]&amp;lt;/tex&amp;gt; - номера деревьев, соответствующих прямым (т.е. &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-я прямая множества, где &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;[1; sz]&amp;lt;/tex&amp;gt; соответствует дереву номер &amp;lt;tex&amp;gt;sz[i]&amp;lt;/tex&amp;gt;). Также воспользуемся тем, что &amp;lt;tex&amp;gt;x[i] = a[i]&amp;lt;/tex&amp;gt; возрастают (по условию задачи), а значит мы можем искать первое такое &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;x[i] \geqslant front[j]&amp;lt;/tex&amp;gt; не бинарным поиском, а методом двух указателей за &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; операций суммарно. Также массив front[] можно хранить в целых числах, округляя х-координаты в сторону лежащих правее по оси x до ближайшего целого (*), т.к. на самом деле мы, считая динамику, подставляем в уравнения прямых только целые &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt;, а значит если &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt;-я прямая пересекается с &amp;lt;tex&amp;gt;k+1&amp;lt;/tex&amp;gt;-й в точке &amp;lt;tex&amp;gt;z +&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\alpha&amp;lt;/tex&amp;gt; (z-целое,  &amp;lt;tex&amp;gt;\alpha&amp;lt;/tex&amp;gt;  &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;[0;1)&amp;lt;/tex&amp;gt;), то мы будем подставлять в их уравнения &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt;  или &amp;lt;tex&amp;gt;z + 1&amp;lt;/tex&amp;gt;. Поэтому можно считать, что новая прямая начинает совпадать с выпуклой оболочкой, начиная с &amp;lt;tex&amp;gt;x = z+1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Реализация==&lt;br /&gt;
  '''int''' &amp;lt;tex&amp;gt;\mathtt{ConvexHullTrick}&amp;lt;/tex&amp;gt;('''int''' a[n], '''int''' c[n])&lt;br /&gt;
     st[1] = 1&lt;br /&gt;
     from[1] = -&amp;lt;tex&amp;gt;\infty&amp;lt;/tex&amp;gt;&amp;lt;font color=green&amp;gt;// первая прямая покрывает все x-ы, начиная с -∞ &amp;lt;/font&amp;gt;&lt;br /&gt;
     sz = 1 &amp;lt;font color=green&amp;gt;// текущий размер выпуклой оболочки &amp;lt;/font&amp;gt;&lt;br /&gt;
     pos = 1 &amp;lt;font color=green&amp;gt;// текущая позиция первого такого j, что x[i] \geqslant front[st[j]] &amp;lt;/font &amp;gt;&lt;br /&gt;
     '''for'''  i = 2..n  &lt;br /&gt;
          '''while''' (front[pos] &amp;lt; x[i]) &amp;lt;font color=green&amp;gt;// метод 1 указателя (ищем первое pos, такое что x[i] покрывается &amp;quot;областью действия&amp;quot; st[pos]-той прямой &amp;lt;/font &amp;gt;&lt;br /&gt;
               pos = pos + 1&lt;br /&gt;
          j = st[pos]&lt;br /&gt;
          dp[i] = K[j]&amp;lt;math&amp;gt;\cdot&amp;lt;/math&amp;gt;a[i] + B[j] &lt;br /&gt;
          '''if''' (i &amp;lt; n)   &amp;lt;font color=green&amp;gt;// если у нас добавляется НЕ последняя прямая, то придется пересчитать выпуклую оболочку &amp;lt;/font &amp;gt;&lt;br /&gt;
                K[i] = c[i]  &amp;lt;font color=green&amp;gt;// наши переобозначения переменных &amp;lt;/font &amp;gt;&lt;br /&gt;
                B[i] = dp[i] &amp;lt;font color=green&amp;gt;// наши переобозначения переменных &amp;lt;/font &amp;gt;&lt;br /&gt;
                x = -&amp;lt;tex&amp;gt;\infty&amp;lt;/tex&amp;gt; &lt;br /&gt;
                '''while''' ''true'' &lt;br /&gt;
                      j = st[sz]&lt;br /&gt;
                      x = divide(B[j] - B[i], K[i] - K[j]) &amp;lt;font color=green&amp;gt;// x-координата пересечения с последней прямой оболочки, округленное в нужную сторону (*) &amp;lt;/font &amp;gt;&lt;br /&gt;
                      '''if''' (x &amp;gt; from[sz]) '''break'''  &amp;lt;font color=green&amp;gt;// перестаем удалять последнюю прямую из множества, если новая прямая пересекает ее позже, чем начинается ее &amp;quot;область действия&amp;quot; &amp;lt;/font &amp;gt;&lt;br /&gt;
                      sz = sz - 1&amp;lt;font color=green&amp;gt;// удаляем последнюю прямую, если она лишняя &amp;lt;/font &amp;gt;&lt;br /&gt;
                 st[sz + 1] = i  &lt;br /&gt;
                 from[sz + 1] = x &amp;lt;font color=green&amp;gt;// добавили новую прямую &amp;lt;/font &amp;gt;&lt;br /&gt;
                 sz = sz + 1&lt;br /&gt;
  '''return''' dp[n] &lt;br /&gt;
Здесь функция &amp;lt;tex&amp;gt;\mathtt{divide}&amp;lt;/tex&amp;gt;(a, b) возвращает нужное(*) округление a / b. Приведем её код :&lt;br /&gt;
 '''int''' &amp;lt;tex&amp;gt;\mathtt{divide}&amp;lt;/tex&amp;gt;('''int''' a, '''int''' b)&lt;br /&gt;
        delta = 0&lt;br /&gt;
        '''if''' (a '''mod''' b ≠ 0) delta = 1&lt;br /&gt;
        '''if''' ((a &amp;gt; 0 '''and''' b &amp;gt; 0) '''or''' (a &amp;lt; 0 '''and''' b &amp;lt; 0)) '''return''' [a / b] + delta&lt;br /&gt;
 '''return''' -[|a| / |b|]&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
Такая реализация будет работать за O(n).&lt;br /&gt;
&lt;br /&gt;
==Динамический convex hull trick==&lt;br /&gt;
	Заметим, что условия на прямые, что &amp;lt;tex&amp;gt;k[i]&amp;lt;/tex&amp;gt; возрастает/убывает и &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt; убывает/возрастает выглядят достаточно редкими для большинства задач. Пусть в задаче таких ограничений нет. Первый способ борьбы с этой проблемой - отсортировать входные данные нужным образом, не испортив свойств задачи (пример : задача G c Санкт-Петербургских сборов к РОИ 2016[http://neerc.ifmo.ru/school/camp-2016/problems/20160318a.pdf]).&lt;br /&gt;
&lt;br /&gt;
	Но рассмотрим общий случай. По-прежнему у нас есть выпуклая оболочка прямых, имея которую мы за &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; можем найти   &amp;lt;tex&amp;gt;dp[i]&amp;lt;/tex&amp;gt;, но теперь вставку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-й прямой в оболочку уже нельзя выполнить описанным ранее способом за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt; в среднем. У нас есть выпуклая оболочка, наша прямая пересекает ее, возможно, «отсекая» несколько отрезков выпуклой оболочки в середине (рис. 4 : красная прямая - та, которую мы хотим вставить в наше множество). Более формально : теперь наша новая прямая будет ниже остальных при &amp;lt;tex&amp;gt;x \in [x1; x2]&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;x1, x2 \in R&amp;lt;/tex&amp;gt; - точки пересечения с некоторыми прямыми, причем &amp;lt;tex&amp;gt;x2&amp;lt;/tex&amp;gt; не обязательно равно &amp;lt;tex&amp;gt;+ \infty&amp;lt;/tex&amp;gt;&lt;br /&gt;
[[Файл:picture4convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
Чтобы уметь вставлять прямую в множество будем хранить &amp;lt;math&amp;gt;std::set&amp;lt;/math&amp;gt; (или любой аналог в других языках программирования) пар &amp;lt;tex&amp;gt;(k, st)&amp;lt;/tex&amp;gt; =  &amp;lt;tex&amp;gt;(коэффицент прямой, ее номер в глобальной нумерации)&amp;lt;/tex&amp;gt;. Когда приходит новая прямая, ищем последнюю прямую с меньшим угловым коэффицентом, чем у той прямой, которую мы хотим добавить в множество. Поиск такой прямой занимает &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt;. Начиная с найденной прямой выполняем &amp;quot;старый&amp;quot; алгоритм (удаляем, пока текущая прямая множества бесполезна). И симметричный алгоритм применяем ко всем прямым справа от нашей (удаляем правого соседа нашей прямой, пока она пересекает нас позже, чем своего правого соседа).&lt;br /&gt;
	&lt;br /&gt;
Асимптотика решения составит &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; на каждый из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; запросов «добавить прямую» + &amp;lt;tex&amp;gt;O(n\cdot\log(n))&amp;lt;/tex&amp;gt; суммарно на удаление прямых, т.к. по-прежнему каждая прямая не более одного раза удалится из множества, а каждое удаление из std::set занимает &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; времени. Итого &amp;lt;math&amp;gt;O(n\cdot\log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Альтернативный подход ==&lt;br /&gt;
Другой способ интерпретировать выражение &amp;lt;tex&amp;gt;dp[i] = \min\limits_{j=0...i-1}(c[j] \cdot a[i] + dp[j])&amp;lt;/tex&amp;gt;  заключается в том, что мы будем пытаться свести задачу к стандартной выпуклой оболочке множества точек. Перепишем выражение средующим образом : &amp;lt;tex&amp;gt;dp[j] + a[i] \cdot c[j] = (dp[j], c[j]) \cdot (1, a[i])&amp;lt;/tex&amp;gt;, т.е. запишем как скалярное произведение векторов &amp;lt;tex&amp;gt;v[j] = (dp[j], c[j])&amp;lt;/tex&amp;gt; и &amp;lt;tex &amp;gt;u[i] = (1, a[i])&amp;lt;/tex &amp;gt;. Вектора  &amp;lt;tex &amp;gt;v[j] =  (dp[j], c[j])&amp;lt;/tex&amp;gt; хотелось бы организовать так, чтобы за &amp;lt;tex &amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; находить вектор, максимизирующий выражение &amp;lt;tex&amp;gt;v[j] \cdot u[i]&amp;lt;/tex&amp;gt;. Посмотрим на рис. 5. Заметим интуитивно очевидный факт : красная точка (вектор) &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; не может давать более оптимальное значение &amp;lt;tex&amp;gt;v[j] \cdot u[i]&amp;lt;/tex&amp;gt; одновременно чем обе синие точки. По этой причине нам достаточно оставить выпуклую оболочку векторов &amp;lt;tex&amp;gt;v[j]&amp;lt;/tex&amp;gt;, а ответ на запрос - это поиск &amp;lt;tex&amp;gt;v[j]&amp;lt;/tex&amp;gt;, максимизирующего проекцию на &amp;lt;tex&amp;gt;u[i]&amp;lt;/tex&amp;gt;. Это задача поиска ближайшей точки выпуклого многоугольника (составленного из точек выпуклой оболочки) к заданной прямой (из &amp;lt;tex&amp;gt;(0, 0)&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;(1, a[i])&amp;lt;/tex&amp;gt;).  Ее можно решить за &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; двумя бинарными или одним тернарным поиском&lt;br /&gt;
Асимптотика алгоритма по-прежнему составит &amp;lt;tex&amp;gt;O(n \cdot \log(n))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Файл:picture5convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
Докажем то, что описанный выше алгоритм корректен. Для этого достаточно показать, что если имеются &amp;lt;math&amp;gt;3&amp;lt;/math&amp;gt; вектора &amp;lt;math&amp;gt;a, b, c&amp;lt;/math&amp;gt;, расположенные как на рис. 5, т.е. точка &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; не лежит на выпуклой оболочке векторов &amp;lt;tex&amp;gt;0, a, b, c &amp;lt;/tex&amp;gt; : &amp;lt;tex&amp;gt; \Leftrightarrow |[a-b, b-c]| &amp;lt; 0 &amp;lt;/tex&amp;gt;, то либо  &amp;lt;tex&amp;gt;(a, u[i])&amp;lt;/tex&amp;gt; оптимальнее, чем &amp;lt;tex&amp;gt;(b, u[i])&amp;lt;/tex&amp;gt;,  либо &amp;lt;tex&amp;gt;(с, u[i])&amp;lt;/tex&amp;gt; оптимальнее, чем &amp;lt;tex&amp;gt;(b, u[i])&amp;lt;/tex&amp;gt;&lt;br /&gt;
{{Теорема&lt;br /&gt;
|id=th12392. &lt;br /&gt;
|statement=Если есть &amp;lt;tex&amp;gt;3&amp;lt;/tex&amp;gt; вектора &amp;lt;tex&amp;gt;a, b, c&amp;lt;/tex&amp;gt;, таких что &amp;lt;tex&amp;gt;|[a-b, b-c]| &amp;lt; 0&amp;lt;/tex&amp;gt; то либо &amp;lt;math&amp;gt;(a, u) &amp;lt; (b, u)&amp;lt;/math&amp;gt;, либо &amp;lt;math&amp;gt;(c, u) &amp;lt; (b, u)&amp;lt;/math&amp;gt;, где вектор &amp;lt;math&amp;gt;u = (1; k)&amp;lt;/math&amp;gt;.&lt;br /&gt;
|proof=&amp;lt;tex&amp;gt;|[a-b, b-c]| &amp;lt; 0 \Leftrightarrow (a_{x} - b_{x})\cdot(b_{y} - c_{y}) &amp;lt; (a_{y} - b_{y}) \cdot (b_{x} - c_{x})&amp;lt;/tex&amp;gt; (*). Предположим (от противного), что &amp;lt;tex&amp;gt;(b, u) &amp;lt; (a, u) \Leftrightarrow b_{x}  + k \cdot b_{y} &amp;lt; c_{x} + k \cdot c_{y} \Leftrightarrow (b_{x} - c_{x}) &amp;lt; k \cdot (c_{y} - b_{y})&amp;lt;/tex&amp;gt; и  &amp;lt;tex&amp;gt;(b, u) &amp;lt; (c, u) \Leftrightarrow b_{x}  + k \cdot b_{y} &amp;lt; a_{x} + k \cdot a_{y} \Leftrightarrow (a_{x} - b_{x}) &amp;gt; k \cdot (b_{y} - a_{y})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Подставим эти неравенства в (*). Получим цепочку неравенств : &amp;lt;tex&amp;gt;k \cdot (a_{y} - b_{y})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (c_{y} - b_{y}) = k&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (b_{y} - a_{y}) \cdot &amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt;(b_{y} - c_{y})&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt; &amp;lt; (a_{x} - b_{x})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (b_{y} - c_{y})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; &amp;lt; (a_{y} - b_{y}) \cdot &amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt;(b_{x} - c_{x})&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;&amp;lt; k \cdot (a_{y} - b_{y})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (c_{y} - b_{y})&amp;lt;/tex&amp;gt;. Получили противоречие : &amp;lt;tex&amp;gt;k \cdot (a_{y} - b_{y}) \cdot (c_{y} - b_{y}) &amp;lt; k \cdot (a_{y} - b_{y}) \cdot (c_{y} - b_{y})&amp;lt;/tex&amp;gt;. Значит предположение неверно, чтд.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Из доказанной теоремы и следует корректность алгоритма.&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
1) http://neerc.ifmo.ru/wiki/index.php?title=Статические_выпуклые_оболочки:_Джарвис,_Грэхем,_Эндрю,_Чен,_QuickHull&lt;br /&gt;
&lt;br /&gt;
2) http://neerc.ifmo.ru/wiki/index.php?title=Динамическое_программирование&lt;br /&gt;
&lt;br /&gt;
[[Категория:Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория: Динамическое программирование]]&lt;br /&gt;
[[Категория: Способы оптимизации методов динамического программирования]]&lt;/div&gt;</summary>
		<author><name>92.255.113.52</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=Convex_hull_trick&amp;diff=60026</id>
		<title>Convex hull trick</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=Convex_hull_trick&amp;diff=60026"/>
				<updated>2017-01-18T17:13:00Z</updated>
		
		<summary type="html">&lt;p&gt;92.255.113.52: /* Альтернативный подход */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Convex hull trick -- один из методов оптимизации динамического программирования [[http://neerc.ifmo.ru/wiki/index.php?title=Динамическое_программирование]], использующий идею выпуклой оболочки. Позволяет улучшить ассимптотику решения некоторых задачь, решемых методом динамического программирования с &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt; до &amp;lt;tex&amp;gt;O(n\cdot\log(n))&amp;lt;/tex&amp;gt;. Техника впервые появилась в 1995 году (задачу на нее предложили в USACO -- национальной олимпиаде США по программированию). Массовую известность получила после IOI (международной олимпиады по программированию для школьников) 2002.&lt;br /&gt;
&lt;br /&gt;
==Пример задачи, решаемой методом convex hull trick==&lt;br /&gt;
Рассмотрим задачу на ДП:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; деревьев с высотами &amp;lt;tex&amp;gt;a_1, a_2, \dots, a_n&amp;lt;/tex&amp;gt; (в метрах). Требуется спилить их все, потратив минимальное количество монет на заправку&lt;br /&gt;
бензопилы. Но пила устроена так, что она может спиливать только по 1 метру от дерева, к которому ее применили. Также после&lt;br /&gt;
срубленного метра (любого дерева) пилу нужно заправлять, платя за  бензин определенной кол-во монет. Причем стоимость &lt;br /&gt;
бензина зависит от срубленных (полностью) деревьев. Если сейчас максимальный индекс срубленного дерева равен &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, то цена заправки &lt;br /&gt;
равна &amp;lt;tex&amp;gt;c_i&amp;lt;/tex&amp;gt;.  Изначально пила заправлена.&lt;br /&gt;
Также известны следующие ограничения : &amp;lt;tex&amp;gt;c_n = 0, a_1 = 1, a_i&amp;lt;/tex&amp;gt; возрастают, &amp;lt;tex&amp;gt;c_i&amp;lt;/tex&amp;gt; убывают. Изначально пила заправлена.&lt;br /&gt;
(убывание и возрастание нестрогие)&lt;br /&gt;
}}&lt;br /&gt;
(Задача H с Санкт-Петербургских сборов к РОИ 2016[http://neerc.ifmo.ru/school/camp-2016/problems/20160318a.pdf])&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
&amp;lt;includeonly&amp;gt;{{#if: {{{neat|}}}|&lt;br /&gt;
&amp;lt;div style=&amp;quot;background-color: #fcfcfc; float:left;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;background-color: #ddd;&amp;quot;&amp;gt;'''Задача:'''&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;border:1px dashed #2f6fab; padding: 8px; font-style: italic;&amp;quot;&amp;gt;{{{definition}}}&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;|&lt;br /&gt;
&amp;lt;table border=&amp;quot;0&amp;quot; width=&amp;quot;100%&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td style=&amp;quot;background-color: #ddd&amp;quot;&amp;gt;'''Задача:'''&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td style=&amp;quot;border:1px dashed #2f6fab; padding: 8px; background-color: #fcfcfc; font-style: italic;&amp;quot;&amp;gt;{{{definition}}}&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;}}&lt;br /&gt;
&amp;lt;/includeonly&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Наивное решение==&lt;br /&gt;
Сначала заметим важный факт : т.к. &amp;lt;tex&amp;gt;c[i]&amp;lt;/tex&amp;gt; убывают (нестрого) и &amp;lt;tex&amp;gt;c[n] = 0&amp;lt;/tex&amp;gt;, то все &amp;lt;tex&amp;gt;c[i]&amp;lt;/tex&amp;gt; неотрицательны.&lt;br /&gt;
Понятно, что нужно затратив минимальную стоимость срубить последнее (&amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;-е) дерево, т.к. после него все деревья можно будет рубить бесплатно (т.к. &amp;lt;tex&amp;gt;c[n] = 0&amp;lt;/tex&amp;gt;). Посчитаем следующую динамику : &amp;lt;tex&amp;gt;dp[i]&amp;lt;/tex&amp;gt; - минимальная стоимость, заплатив которую можно добиться того, что дерево номер &amp;lt;tex&amp;gt;i.&amp;lt;/tex&amp;gt; будет срублено.&lt;br /&gt;
База динамики : &amp;lt;tex&amp;gt;dp[1] = 0&amp;lt;/tex&amp;gt;, т.к. изначально пила заправлена и высота первого дерева равна 1, по условию задачи.&lt;br /&gt;
Переход динамики :  понятно, что выгодно рубить сначала более дорогие и низкие деревья, а потом более высокие и дешевые (док-во этого факта оставляется читателям как несложное упражнение, т.к. эта идея относится скорее к теме жадных алгоритмнов, чем к теме данной статьи). Поэтому перед  &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-м деревом мы обязательно срубили какое-то &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-е, причем &amp;lt;tex&amp;gt;j \leqslant i - 1&amp;lt;/tex&amp;gt;. Поэтому чтобы найти &amp;lt;tex&amp;gt;dp[i]&amp;lt;/tex&amp;gt; нужно перебрать все &amp;lt;tex&amp;gt;1 \leqslant j \leqslant i - 1&amp;lt;/tex&amp;gt; и попытаться использовать ответ для дерева намер &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;. Итак, пусть перед &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-м деревом мы полностью срубили &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-е, причем высота &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева составляет &amp;lt;tex&amp;gt;a[i]&amp;lt;/tex&amp;gt;, а т.к. последнее дерево, которое мы срубили имеет индекс &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, то стоимость каждого метра &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева составит &amp;lt;tex&amp;gt;c[j]&amp;lt;/tex&amp;gt;.  Поэтому на сруб &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева мы потратим &amp;lt;tex&amp;gt;a[i] \cdot c[j]&amp;lt;/tex&amp;gt; монет. Также не стоит забывать, ситуацию, когда  &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-е дерево полностью срублено, мы получили не бесплатно, а за &amp;lt;tex&amp;gt;dp[j]&amp;lt;/tex&amp;gt; монет.&lt;br /&gt;
Итогвая формула пересчета : &amp;lt;tex&amp;gt;dp[i] = \min\limits_{j=1...i-1} (dp[j] + a[i] \cdot c[j])&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Посмотрим на код выше описанного решения:&lt;br /&gt;
 '''int''' &amp;lt;tex&amp;gt;\mathtt{simpleDP}&amp;lt;/tex&amp;gt;('''int''' a[n], '''int''' c[n])&lt;br /&gt;
    dp[1] = 0&lt;br /&gt;
    dp[2] = dp[3] = ... = dp[n] = &amp;lt;tex&amp;gt;\infty&amp;lt;/tex&amp;gt;&lt;br /&gt;
    '''for'''  i = 1..n-1  &lt;br /&gt;
       dp[i] = &amp;lt;tex&amp;gt;+\infty&amp;lt;/tex&amp;gt;&lt;br /&gt;
       '''for''' j = 0..i-1 &lt;br /&gt;
           '''if''' (dp[j] + a[i] &amp;lt;tex&amp;gt;\cdot&amp;lt;/tex&amp;gt; c[j] &amp;lt; dp[i])&lt;br /&gt;
           dp[i] = dp[j] + a[i] &amp;lt;tex&amp;gt;\cdot&amp;lt;/tex&amp;gt; c[j]&lt;br /&gt;
 '''return''' dp[n]&lt;br /&gt;
Нетрудно видеть, что такая динамика работает за &amp;lt;tex&amp;gt;O(n^2)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Ключевая идея оптимизации==&lt;br /&gt;
Для начала сделаем замену обозначений. Давайте обозначим &amp;lt;tex&amp;gt;dp[j]&amp;lt;/tex&amp;gt; за &amp;lt;tex&amp;gt;b[j]&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;a[i]&amp;lt;/tex&amp;gt; за &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt;, а &amp;lt;tex&amp;gt;c[j]&amp;lt;/tex&amp;gt; за &amp;lt;tex&amp;gt;k[j]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь формула приняла вид &amp;lt;tex&amp;gt;dp[i] = \min\limits_{j=0...i-1}(k[j] \cdot x[i] + b[j])&amp;lt;/tex&amp;gt;. Выражение &amp;lt;tex&amp;gt;k[j] \cdot x + b[j]&amp;lt;/tex&amp;gt; - это в точности уравнение прямой вида &amp;lt;tex&amp;gt;y = kx + b&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сопоставим каждому &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, обработанному ранее, прямую &amp;lt;tex&amp;gt;y[j](x) = k[j] \cdot x + b[j]&amp;lt;/tex&amp;gt;. Из условия «&amp;lt;tex&amp;gt;c[i]&amp;lt;/tex&amp;gt; убывают &amp;lt;tex&amp;gt;\Leftrightarrow  k[j]&amp;lt;/tex&amp;gt; уменьшаются с номером &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;» следует то, что прямые, полученные ранее отсортированы в порядке убывания углового коэффициент. Давайте нарисуем несколько таких прямых :&lt;br /&gt;
&lt;br /&gt;
[[Файл:picture1convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
Выделим множество точек &amp;lt;tex&amp;gt;(x0, y0)&amp;lt;/tex&amp;gt; , таких что все они принадлежат одной из прямых и при этом нету ни одной прямой &amp;lt;tex&amp;gt;y’(x)&amp;lt;/tex&amp;gt;, такой что &amp;lt;tex&amp;gt;y’(x0) &amp;lt; y0&amp;lt;/tex&amp;gt;. Иными словами возьмем «выпуклую (вверх) оболочку» нашего множества прямых (её еще называют нижней ошибающей множества прямых на плоскости). Назовем ее «&amp;lt;tex&amp;gt;y = convex(x)&amp;lt;/tex&amp;gt;». Видно, что множество точек &amp;lt;math&amp;gt;(x, convex(x))&amp;lt;/math&amp;gt; представляет собой выпуклую вверх функцию.&lt;br /&gt;
&lt;br /&gt;
==Цель нижней огибающей множества прямых==&lt;br /&gt;
Пусть мы считаем динамику для &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева. Его задает &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt;. Итак, нам нужно для данного &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt; найти &amp;lt;tex&amp;gt;\min\limits_{j=0..i-1}(k[j] \cdot x[i] + b[i]) = \min\limits_{j=0..i-1}(y[j](x[i]))&amp;lt;/tex&amp;gt;. Это выражение есть &amp;lt;math&amp;gt;convex(x[i])&amp;lt;/math&amp;gt;. Из монотонности угловых коэффицентов отрезков, задающих выпуклую оболочку, и их расположения по координаты x следует то, что отрезок, который пересекает прямую &amp;lt;tex&amp;gt;x = x[i]&amp;lt;/tex&amp;gt;, можно найти бинарным поиском. Это потребует &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; времени на поиск такого &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;dp[i] = k[j] \cdot x[i] + b[j]&amp;lt;/tex&amp;gt;. Теперь осталось научиться поддерживать множество прямых и быстро добавлять &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-ю прямую после того, как мы посчитали &amp;lt;tex&amp;gt;b[i] = dp[i]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Воспользуемся идеей алгоритма построения выпуклой оболочки множества точек. Заведем 2 стека &amp;lt;tex&amp;gt;k[]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;b[]&amp;lt;/tex&amp;gt;, которые задают прямые в отсортированном порядке их угловыми коэффицентами и свободными членами. Рассмотрим ситуацию, когда мы хотим добавить новую (&amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-тую) прямую в множество. Пусть сейчас в множестве лежит &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt; прямых (нумерация с 1). Пусть &amp;lt;tex&amp;gt;(xL, yL)&amp;lt;/tex&amp;gt; - точка пересечения &amp;lt;tex&amp;gt;sz - 1&amp;lt;/tex&amp;gt;-й прямой множества и &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-й, а &amp;lt;tex&amp;gt;(xR, yR)&amp;lt;/tex&amp;gt; - точка пересечения новой прямой, которую мы хотим добавить в конец множества и &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-й. Нас будут интересовать только их &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;-овые координаты &amp;lt;tex&amp;gt;xL&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;xR&amp;lt;/tex&amp;gt;, соответственно. Если оказалось, что новая прямая пересекает &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-ю прямую выпуклой оболочки позже, чем &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-я &amp;lt;tex&amp;gt;sz - 1&amp;lt;/tex&amp;gt;-ю, т.е. &amp;lt;tex&amp;gt;(xL \geqslant xR)&amp;lt;/tex&amp;gt;, то &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-ю удалим из нашего множества, иначе - остановимся. Так будем делать, пока либо кол-во прямых в стеке не станет равным 2, либо &amp;lt;tex&amp;gt;xL&amp;lt;/tex&amp;gt; не станет меньше &amp;lt;tex&amp;gt;xR.&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Асимптотика : аналогично обычному алгоритму построения выпуклой оболочки, каждая прямая ровно &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; раз добавится в стек и максимум &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; раз удалится. Значит время работы перестройки выпуклой оболочки займет &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; суммарно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:picture2convexhull.png]]&lt;br /&gt;
[[Файл:picture3convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
{{Теорема&lt;br /&gt;
|id=th1239. &lt;br /&gt;
|statement=Алгоритм построения нижней огибающей множества прямых корректен.&lt;br /&gt;
|proof=Достаточно показать, что последнюю прямую нужно удалить из множества т.и т.т., когда она наша новая прямая пересекает ее в точке с координатой по оси X, меньшей, чем последняя - предпоследнюю.&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;Y(x) = Kx + B&amp;lt;/tex&amp;gt; - уравнение новой прямой,  &amp;lt;tex&amp;gt;y[i](x) = K[i]x + B[i]&amp;lt;/tex&amp;gt; - уравнения прямых множества. Тогда т.к. &amp;lt;tex&amp;gt;K &amp;lt; K[sz]&amp;lt;/tex&amp;gt;, то при &amp;lt;tex&amp;gt;x \in [- \infty; xR]  : y[sz](x) &amp;lt;= Y(x)&amp;lt;/tex&amp;gt;, а т.к. &amp;lt;tex&amp;gt; K[sz] &amp;lt; K[sz - 1]&amp;lt;/tex&amp;gt;, то при &amp;lt;tex&amp;gt;x \in [xL; + \infty]  : y[sz - 1](x) \geqslant y[sz](x)&amp;lt;/tex&amp;gt;. Если &amp;lt;tex&amp;gt;xL &amp;lt; xR&amp;lt;/tex&amp;gt;, то при &amp;lt;tex&amp;gt;x \in [xL; xR]  : y[sz - 1] \geqslant y[sz](x) и Y(x) \geqslant y[sz](x)&amp;lt;/tex&amp;gt;, т.е. на отрезке &amp;lt;tex&amp;gt;[xL; xR]&amp;lt;/tex&amp;gt; прямая номер sz лежит ниже остальных и её нужно оставить в множестве. Если же &amp;lt;tex&amp;gt;xL &amp;gt; xR&amp;lt;/tex&amp;gt;, то она ниже всех на отрезке &amp;lt;tex&amp;gt;[xL; xR] = \varnothing &amp;lt;/tex&amp;gt;, т.е. её можно удалить из множества&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==Детали реализации:==&lt;br /&gt;
Будем хранить 2 массива : &amp;lt;tex&amp;gt;front[]&amp;lt;/tex&amp;gt; - &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;-координаты, начиная с которых прямые совпадают с выпуклой оболочкой (т.е. i-я прямая совпадает с выпуклой оболочкой текущего множества прямых при &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;[front[i]; front[i + 1])&amp;lt;/tex&amp;gt; ) и &amp;lt;tex&amp;gt;st[]&amp;lt;/tex&amp;gt; - номера деревьев, соответствующих прямым (т.е. &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-я прямая множества, где &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;[1; sz]&amp;lt;/tex&amp;gt; соответствует дереву номер &amp;lt;tex&amp;gt;sz[i]&amp;lt;/tex&amp;gt;). Также воспользуемся тем, что &amp;lt;tex&amp;gt;x[i] = a[i]&amp;lt;/tex&amp;gt; возрастают (по условию задачи), а значит мы можем искать первое такое &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;x[i] \geqslant front[j]&amp;lt;/tex&amp;gt; не бинарным поиском, а методом двух указателей за &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; операций суммарно. Также массив front[] можно хранить в целых числах, округляя х-координаты в сторону лежащих правее по оси x до ближайшего целого (*), т.к. на самом деле мы, считая динамику, подставляем в уравнения прямых только целые &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt;, а значит если &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt;-я прямая пересекается с &amp;lt;tex&amp;gt;k+1&amp;lt;/tex&amp;gt;-й в точке &amp;lt;tex&amp;gt;z +&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\alpha&amp;lt;/tex&amp;gt; (z-целое,  &amp;lt;tex&amp;gt;\alpha&amp;lt;/tex&amp;gt;  &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;[0;1)&amp;lt;/tex&amp;gt;), то мы будем подставлять в их уравнения &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt;  или &amp;lt;tex&amp;gt;z + 1&amp;lt;/tex&amp;gt;. Поэтому можно считать, что новая прямая начинает совпадать с выпуклой оболочкой, начиная с &amp;lt;tex&amp;gt;x = z+1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Реализация==&lt;br /&gt;
  '''int''' &amp;lt;tex&amp;gt;\mathtt{ConvexHullTrick}&amp;lt;/tex&amp;gt;('''int''' a[n], '''int''' c[n])&lt;br /&gt;
     st[1] = 1&lt;br /&gt;
     from[1] = -&amp;lt;tex&amp;gt;\infty&amp;lt;/tex&amp;gt;&amp;lt;font color=green&amp;gt;// первая прямая покрывает все x-ы, начиная с -∞ &amp;lt;/font&amp;gt;&lt;br /&gt;
     sz = 1 &amp;lt;font color=green&amp;gt;// текущий размер выпуклой оболочки &amp;lt;/font&amp;gt;&lt;br /&gt;
     pos = 1 &amp;lt;font color=green&amp;gt;// текущая позиция первого такого j, что x[i] \geqslant front[st[j]] &amp;lt;/font &amp;gt;&lt;br /&gt;
     '''for'''  i = 2..n  &lt;br /&gt;
          '''while''' (front[pos] &amp;lt; x[i]) &amp;lt;font color=green&amp;gt;// метод 1 указателя (ищем первое pos, такое что x[i] покрывается &amp;quot;областью действия&amp;quot; st[pos]-той прямой &amp;lt;/font &amp;gt;&lt;br /&gt;
               pos = pos + 1&lt;br /&gt;
          j = st[pos]&lt;br /&gt;
          dp[i] = K[j]&amp;lt;math&amp;gt;\cdot&amp;lt;/math&amp;gt;a[i] + B[j] &lt;br /&gt;
          '''if''' (i &amp;lt; n)   &amp;lt;font color=green&amp;gt;// если у нас добавляется НЕ последняя прямая, то придется пересчитать выпуклую оболочку &amp;lt;/font &amp;gt;&lt;br /&gt;
                K[i] = c[i]  &amp;lt;font color=green&amp;gt;// наши переобозначения переменных &amp;lt;/font &amp;gt;&lt;br /&gt;
                B[i] = dp[i] &amp;lt;font color=green&amp;gt;// наши переобозначения переменных &amp;lt;/font &amp;gt;&lt;br /&gt;
                x = -&amp;lt;tex&amp;gt;\infty&amp;lt;/tex&amp;gt; &lt;br /&gt;
                '''while''' ''true'' &lt;br /&gt;
                      j = st[sz]&lt;br /&gt;
                      x = divide(B[j] - B[i], K[i] - K[j]) &amp;lt;font color=green&amp;gt;// x-координата пересечения с последней прямой оболочки, округленное в нужную сторону (*) &amp;lt;/font &amp;gt;&lt;br /&gt;
                      '''if''' (x &amp;gt; from[sz]) '''break'''  &amp;lt;font color=green&amp;gt;// перестаем удалять последнюю прямую из множества, если новая прямая пересекает ее позже, чем начинается ее &amp;quot;область действия&amp;quot; &amp;lt;/font &amp;gt;&lt;br /&gt;
                      sz = sz - 1&amp;lt;font color=green&amp;gt;// удаляем последнюю прямую, если она лишняя &amp;lt;/font &amp;gt;&lt;br /&gt;
                 st[sz + 1] = i  &lt;br /&gt;
                 from[sz + 1] = x &amp;lt;font color=green&amp;gt;// добавили новую прямую &amp;lt;/font &amp;gt;&lt;br /&gt;
                 sz = sz + 1&lt;br /&gt;
  '''return''' dp[n] &lt;br /&gt;
Здесь функция &amp;lt;tex&amp;gt;\mathtt{divide}&amp;lt;/tex&amp;gt;(a, b) возвращает нужное(*) округление a / b. Приведем её код :&lt;br /&gt;
 '''int''' &amp;lt;tex&amp;gt;\mathtt{divide}&amp;lt;/tex&amp;gt;('''int''' a, '''int''' b)&lt;br /&gt;
        delta = 0&lt;br /&gt;
        '''if''' (a '''mod''' b ≠ 0) delta = 1&lt;br /&gt;
        '''if''' ((a &amp;gt; 0 '''and''' b &amp;gt; 0) '''or''' (a &amp;lt; 0 '''and''' b &amp;lt; 0)) '''return''' [a / b] + delta&lt;br /&gt;
 '''return''' -[|a| / |b|]&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
Такая реализация будет работать за O(n).&lt;br /&gt;
&lt;br /&gt;
==Динамический convex hull trick==&lt;br /&gt;
	Заметим, что условия на прямые, что &amp;lt;tex&amp;gt;k[i]&amp;lt;/tex&amp;gt; возрастает/убывает и &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt; убывает/возрастает выглядят достаточно редкими для большинства задач. Пусть в задаче таких ограничений нет. Первый способ борьбы с этой проблемой - отсортировать входные данные нужным образом, не испортив свойств задачи (пример : задача G c Санкт-Петербургских сборов к РОИ 2016[http://neerc.ifmo.ru/school/camp-2016/problems/20160318a.pdf]).&lt;br /&gt;
&lt;br /&gt;
	Но рассмотрим общий случай. По-прежнему у нас есть выпуклая оболочка прямых, имея которую мы за &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; можем найти   &amp;lt;tex&amp;gt;dp[i]&amp;lt;/tex&amp;gt;, но теперь вставку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-й прямой в оболочку уже нельзя выполнить описанным ранее способом за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt; в среднем. У нас есть выпуклая оболочка, наша прямая пересекает ее, возможно, «отсекая» несколько отрезков выпуклой оболочки в середине (рис. 4 : красная прямая - та, которую мы хотим вставить в наше множество). Более формально : теперь наша новая прямая будет ниже остальных при &amp;lt;tex&amp;gt;x \in [x1; x2]&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;x1, x2 \in R&amp;lt;/tex&amp;gt; - точки пересечения с некоторыми прямыми, причем &amp;lt;tex&amp;gt;x2&amp;lt;/tex&amp;gt; не обязательно равно &amp;lt;tex&amp;gt;+ \infty&amp;lt;/tex&amp;gt;&lt;br /&gt;
[[Файл:picture4convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
Чтобы уметь вставлять прямую в множество будем хранить &amp;lt;math&amp;gt;std::set&amp;lt;/math&amp;gt; (или любой аналог в других языках программирования) пар &amp;lt;tex&amp;gt;(k, st)&amp;lt;/tex&amp;gt; =  &amp;lt;tex&amp;gt;(коэффицент прямой, ее номер в глобальной нумерации)&amp;lt;/tex&amp;gt;. Когда приходит новая прямая, ищем последнюю прямую с меньшим угловым коэффицентом, чем у той прямой, которую мы хотим добавить в множество. Поиск такой прямой занимает &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt;. Начиная с найденной прямой выполняем &amp;quot;старый&amp;quot; алгоритм (удаляем, пока текущая прямая множества бесполезна). И симметричный алгоритм применяем ко всем прямым справа от нашей (удаляем правого соседа нашей прямой, пока она пересекает нас позже, чем своего правого соседа).&lt;br /&gt;
	&lt;br /&gt;
Асимптотика решения составит &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; на каждый из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; запросов «добавить прямую» + &amp;lt;tex&amp;gt;O(n\cdot\log(n))&amp;lt;/tex&amp;gt; суммарно на удаление прямых, т.к. по-прежнему каждая прямая не более одного раза удалится из множества, а каждое удаление из std::set занимает &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; времени. Итого &amp;lt;math&amp;gt;O(n\cdot\log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Альтернативный подход ==&lt;br /&gt;
Другой способ интерпретировать выражение &amp;lt;tex&amp;gt;dp[i] = \min\limits_{j=0...i-1}(c[j] \cdot a[i] + dp[j])&amp;lt;/tex&amp;gt;  заключается в том, что мы будем пытаться свести задачу к стандартной выпуклой оболочке множества точек. Перепишем выражение средующим образом : &amp;lt;tex&amp;gt;dp[j] + a[i] \cdot c[j] = (dp[j], c[j]) \cdot (1, a[i])&amp;lt;/tex&amp;gt;, т.е. запишем как скалярное произведение векторов &amp;lt;tex&amp;gt;v[j] = (dp[j], c[j])&amp;lt;/tex&amp;gt; и &amp;lt;tex &amp;gt;u[i] = (1, a[i])&amp;lt;/tex &amp;gt;. Вектора  &amp;lt;tex &amp;gt;v[j] =  (dp[j], c[j])&amp;lt;/tex&amp;gt; хотелось бы организовать так, чтобы за &amp;lt;tex &amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; находить вектор, максимизирующий выражение &amp;lt;tex&amp;gt;v[j] \cdot u[i]&amp;lt;/tex&amp;gt;. Посмотрим на рис. 5. Заметим интуитивно очевидный факт : красная точка (вектор) &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; не может давать более оптимальное значение &amp;lt;tex&amp;gt;v[j] \cdot u[i]&amp;lt;/tex&amp;gt; одновременно чем обе синие точки. По этой причине нам достаточно оставить выпуклую оболочку векторов &amp;lt;tex&amp;gt;v[j]&amp;lt;/tex&amp;gt;, а ответ на запрос - это поиск &amp;lt;tex&amp;gt;v[j]&amp;lt;/tex&amp;gt;, максимизирующего проекцию на &amp;lt;tex&amp;gt;u[i]&amp;lt;/tex&amp;gt;. Это задача поиска ближайшей точки выпуклого многоугольника (составленного из точек выпуклой оболочки) к заданной прямой (из &amp;lt;tex&amp;gt;(0, 0)&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;(1, a[i])&amp;lt;/tex&amp;gt;).  Ее можно решить за &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; двумя бинарными или одним тернарным поиском&lt;br /&gt;
Асимптотика алгоритма по-прежнему составит &amp;lt;tex&amp;gt;O(n \cdot \log(n))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Файл:picture5convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
Докажем то, что описанный выше алгоритм корректен. Для этого достаточно показать, что если имеются 3 вектора a, b, c, расположенные как на рис. 5, т.е. точка b не лежит на выпуклой оболочке векторов &amp;lt;tex&amp;gt;0, a, b, c &amp;lt;/tex&amp;gt; : &amp;lt;tex&amp;gt; \Leftrightarrow |[a-b, b-c]| &amp;lt; 0 &amp;lt;/tex&amp;gt;, то либо  &amp;lt;tex&amp;gt;(a, u[i])&amp;lt;/tex&amp;gt; оптимальнее, чем &amp;lt;tex&amp;gt;(b, u[i])&amp;lt;/tex&amp;gt;,  либо &amp;lt;tex&amp;gt;(с, u[i])&amp;lt;/tex&amp;gt; оптимальнее, чем &amp;lt;tex&amp;gt;(b, u[i])&amp;lt;/tex&amp;gt;&lt;br /&gt;
{{Теорема&lt;br /&gt;
|id=th12392. &lt;br /&gt;
|statement=Если есть &amp;lt;tex&amp;gt;3&amp;lt;/tex&amp;gt; вектора &amp;lt;tex&amp;gt;a, b, c&amp;lt;/tex&amp;gt;, таких что &amp;lt;tex&amp;gt;|[a-b, b-c]| &amp;lt; 0&amp;lt;/tex&amp;gt; то либо &amp;lt;math&amp;gt;(a, u) &amp;lt; (b, u)&amp;lt;/math&amp;gt;, либо &amp;lt;math&amp;gt;(c, u) &amp;lt; (b, u)&amp;lt;/math&amp;gt;, где вектор &amp;lt;math&amp;gt;u = (1; k)&amp;lt;/math&amp;gt;.&lt;br /&gt;
|proof=&amp;lt;tex&amp;gt;|[a-b, b-c]| &amp;lt; 0 \Leftrightarrow (a_{x} - b_{x})\cdot(b_{y} - c_{y}) &amp;lt; (a_{y} - b_{y}) \cdot (b_{x} - c_{x})&amp;lt;/tex&amp;gt; (*). Предположим (от противного), что &amp;lt;tex&amp;gt;(b, u) &amp;lt; (a, u) \Leftrightarrow b_{x}  + k \cdot b_{y} &amp;lt; c_{x} + k \cdot c_{y} \Leftrightarrow (b_{x} - c_{x}) &amp;lt; k \cdot (c_{y} - b_{y})&amp;lt;/tex&amp;gt; и  &amp;lt;tex&amp;gt;(b, u) &amp;lt; (c, u) \Leftrightarrow b_{x}  + k \cdot b_{y} &amp;lt; a_{x} + k \cdot a_{y} \Leftrightarrow (a_{x} - b_{x}) &amp;gt; k \cdot (b_{y} - a_{y})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Подставим эти неравенства в (*). Получим цепочку неравенств : &amp;lt;tex&amp;gt;k \cdot (a_{y} - b_{y})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (c_{y} - b_{y}) = k&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (b_{y} - a_{y}) \cdot &amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt;(b_{y} - c_{y})&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt; &amp;lt; (a_{x} - b_{x})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (b_{y} - c_{y})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; &amp;lt; (a_{y} - b_{y}) \cdot &amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt;(b_{x} - c_{x})&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;&amp;lt; k \cdot (a_{y} - b_{y})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (c_{y} - b_{y})&amp;lt;/tex&amp;gt;. Получили противоречие : &amp;lt;tex&amp;gt;k \cdot (a_{y} - b_{y}) \cdot (c_{y} - b_{y}) &amp;lt; k \cdot (a_{y} - b_{y}) \cdot (c_{y} - b_{y})&amp;lt;/tex&amp;gt;. Значит предположение неверно, чтд.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Из доказанной теоремы и следует корректность алгоритма.&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
1) http://neerc.ifmo.ru/wiki/index.php?title=Статические_выпуклые_оболочки:_Джарвис,_Грэхем,_Эндрю,_Чен,_QuickHull&lt;br /&gt;
&lt;br /&gt;
2) http://neerc.ifmo.ru/wiki/index.php?title=Динамическое_программирование&lt;br /&gt;
&lt;br /&gt;
[[Категория:Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория: Динамическое программирование]]&lt;br /&gt;
[[Категория: Способы оптимизации методов динамического программирования]]&lt;/div&gt;</summary>
		<author><name>92.255.113.52</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=Convex_hull_trick&amp;diff=60025</id>
		<title>Convex hull trick</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=Convex_hull_trick&amp;diff=60025"/>
				<updated>2017-01-18T17:07:42Z</updated>
		
		<summary type="html">&lt;p&gt;92.255.113.52: /* Альтернативный подход */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Convex hull trick -- один из методов оптимизации динамического программирования [[http://neerc.ifmo.ru/wiki/index.php?title=Динамическое_программирование]], использующий идею выпуклой оболочки. Позволяет улучшить ассимптотику решения некоторых задачь, решемых методом динамического программирования с &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt; до &amp;lt;tex&amp;gt;O(n\cdot\log(n))&amp;lt;/tex&amp;gt;. Техника впервые появилась в 1995 году (задачу на нее предложили в USACO -- национальной олимпиаде США по программированию). Массовую известность получила после IOI (международной олимпиады по программированию для школьников) 2002.&lt;br /&gt;
&lt;br /&gt;
==Пример задачи, решаемой методом convex hull trick==&lt;br /&gt;
Рассмотрим задачу на ДП:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; деревьев с высотами &amp;lt;tex&amp;gt;a_1, a_2, \dots, a_n&amp;lt;/tex&amp;gt; (в метрах). Требуется спилить их все, потратив минимальное количество монет на заправку&lt;br /&gt;
бензопилы. Но пила устроена так, что она может спиливать только по 1 метру от дерева, к которому ее применили. Также после&lt;br /&gt;
срубленного метра (любого дерева) пилу нужно заправлять, платя за  бензин определенной кол-во монет. Причем стоимость &lt;br /&gt;
бензина зависит от срубленных (полностью) деревьев. Если сейчас максимальный индекс срубленного дерева равен &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, то цена заправки &lt;br /&gt;
равна &amp;lt;tex&amp;gt;c_i&amp;lt;/tex&amp;gt;.  Изначально пила заправлена.&lt;br /&gt;
Также известны следующие ограничения : &amp;lt;tex&amp;gt;c_n = 0, a_1 = 1, a_i&amp;lt;/tex&amp;gt; возрастают, &amp;lt;tex&amp;gt;c_i&amp;lt;/tex&amp;gt; убывают. Изначально пила заправлена.&lt;br /&gt;
(убывание и возрастание нестрогие)&lt;br /&gt;
}}&lt;br /&gt;
(Задача H с Санкт-Петербургских сборов к РОИ 2016[http://neerc.ifmo.ru/school/camp-2016/problems/20160318a.pdf])&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
&amp;lt;includeonly&amp;gt;{{#if: {{{neat|}}}|&lt;br /&gt;
&amp;lt;div style=&amp;quot;background-color: #fcfcfc; float:left;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;background-color: #ddd;&amp;quot;&amp;gt;'''Задача:'''&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;border:1px dashed #2f6fab; padding: 8px; font-style: italic;&amp;quot;&amp;gt;{{{definition}}}&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;|&lt;br /&gt;
&amp;lt;table border=&amp;quot;0&amp;quot; width=&amp;quot;100%&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td style=&amp;quot;background-color: #ddd&amp;quot;&amp;gt;'''Задача:'''&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td style=&amp;quot;border:1px dashed #2f6fab; padding: 8px; background-color: #fcfcfc; font-style: italic;&amp;quot;&amp;gt;{{{definition}}}&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;}}&lt;br /&gt;
&amp;lt;/includeonly&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Наивное решение==&lt;br /&gt;
Сначала заметим важный факт : т.к. &amp;lt;tex&amp;gt;c[i]&amp;lt;/tex&amp;gt; убывают (нестрого) и &amp;lt;tex&amp;gt;c[n] = 0&amp;lt;/tex&amp;gt;, то все &amp;lt;tex&amp;gt;c[i]&amp;lt;/tex&amp;gt; неотрицательны.&lt;br /&gt;
Понятно, что нужно затратив минимальную стоимость срубить последнее (&amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;-е) дерево, т.к. после него все деревья можно будет рубить бесплатно (т.к. &amp;lt;tex&amp;gt;c[n] = 0&amp;lt;/tex&amp;gt;). Посчитаем следующую динамику : &amp;lt;tex&amp;gt;dp[i]&amp;lt;/tex&amp;gt; - минимальная стоимость, заплатив которую можно добиться того, что дерево номер &amp;lt;tex&amp;gt;i.&amp;lt;/tex&amp;gt; будет срублено.&lt;br /&gt;
База динамики : &amp;lt;tex&amp;gt;dp[1] = 0&amp;lt;/tex&amp;gt;, т.к. изначально пила заправлена и высота первого дерева равна 1, по условию задачи.&lt;br /&gt;
Переход динамики :  понятно, что выгодно рубить сначала более дорогие и низкие деревья, а потом более высокие и дешевые (док-во этого факта оставляется читателям как несложное упражнение, т.к. эта идея относится скорее к теме жадных алгоритмнов, чем к теме данной статьи). Поэтому перед  &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-м деревом мы обязательно срубили какое-то &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-е, причем &amp;lt;tex&amp;gt;j \leqslant i - 1&amp;lt;/tex&amp;gt;. Поэтому чтобы найти &amp;lt;tex&amp;gt;dp[i]&amp;lt;/tex&amp;gt; нужно перебрать все &amp;lt;tex&amp;gt;1 \leqslant j \leqslant i - 1&amp;lt;/tex&amp;gt; и попытаться использовать ответ для дерева намер &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;. Итак, пусть перед &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-м деревом мы полностью срубили &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-е, причем высота &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева составляет &amp;lt;tex&amp;gt;a[i]&amp;lt;/tex&amp;gt;, а т.к. последнее дерево, которое мы срубили имеет индекс &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, то стоимость каждого метра &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева составит &amp;lt;tex&amp;gt;c[j]&amp;lt;/tex&amp;gt;.  Поэтому на сруб &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева мы потратим &amp;lt;tex&amp;gt;a[i] \cdot c[j]&amp;lt;/tex&amp;gt; монет. Также не стоит забывать, ситуацию, когда  &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-е дерево полностью срублено, мы получили не бесплатно, а за &amp;lt;tex&amp;gt;dp[j]&amp;lt;/tex&amp;gt; монет.&lt;br /&gt;
Итогвая формула пересчета : &amp;lt;tex&amp;gt;dp[i] = \min\limits_{j=1...i-1} (dp[j] + a[i] \cdot c[j])&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Посмотрим на код выше описанного решения:&lt;br /&gt;
 '''int''' &amp;lt;tex&amp;gt;\mathtt{simpleDP}&amp;lt;/tex&amp;gt;('''int''' a[n], '''int''' c[n])&lt;br /&gt;
    dp[1] = 0&lt;br /&gt;
    dp[2] = dp[3] = ... = dp[n] = &amp;lt;tex&amp;gt;\infty&amp;lt;/tex&amp;gt;&lt;br /&gt;
    '''for'''  i = 1..n-1  &lt;br /&gt;
       dp[i] = &amp;lt;tex&amp;gt;+\infty&amp;lt;/tex&amp;gt;&lt;br /&gt;
       '''for''' j = 0..i-1 &lt;br /&gt;
           '''if''' (dp[j] + a[i] &amp;lt;tex&amp;gt;\cdot&amp;lt;/tex&amp;gt; c[j] &amp;lt; dp[i])&lt;br /&gt;
           dp[i] = dp[j] + a[i] &amp;lt;tex&amp;gt;\cdot&amp;lt;/tex&amp;gt; c[j]&lt;br /&gt;
 '''return''' dp[n]&lt;br /&gt;
Нетрудно видеть, что такая динамика работает за &amp;lt;tex&amp;gt;O(n^2)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Ключевая идея оптимизации==&lt;br /&gt;
Для начала сделаем замену обозначений. Давайте обозначим &amp;lt;tex&amp;gt;dp[j]&amp;lt;/tex&amp;gt; за &amp;lt;tex&amp;gt;b[j]&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;a[i]&amp;lt;/tex&amp;gt; за &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt;, а &amp;lt;tex&amp;gt;c[j]&amp;lt;/tex&amp;gt; за &amp;lt;tex&amp;gt;k[j]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь формула приняла вид &amp;lt;tex&amp;gt;dp[i] = \min\limits_{j=0...i-1}(k[j] \cdot x[i] + b[j])&amp;lt;/tex&amp;gt;. Выражение &amp;lt;tex&amp;gt;k[j] \cdot x + b[j]&amp;lt;/tex&amp;gt; - это в точности уравнение прямой вида &amp;lt;tex&amp;gt;y = kx + b&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сопоставим каждому &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, обработанному ранее, прямую &amp;lt;tex&amp;gt;y[j](x) = k[j] \cdot x + b[j]&amp;lt;/tex&amp;gt;. Из условия «&amp;lt;tex&amp;gt;c[i]&amp;lt;/tex&amp;gt; убывают &amp;lt;tex&amp;gt;\Leftrightarrow  k[j]&amp;lt;/tex&amp;gt; уменьшаются с номером &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;» следует то, что прямые, полученные ранее отсортированы в порядке убывания углового коэффициент. Давайте нарисуем несколько таких прямых :&lt;br /&gt;
&lt;br /&gt;
[[Файл:picture1convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
Выделим множество точек &amp;lt;tex&amp;gt;(x0, y0)&amp;lt;/tex&amp;gt; , таких что все они принадлежат одной из прямых и при этом нету ни одной прямой &amp;lt;tex&amp;gt;y’(x)&amp;lt;/tex&amp;gt;, такой что &amp;lt;tex&amp;gt;y’(x0) &amp;lt; y0&amp;lt;/tex&amp;gt;. Иными словами возьмем «выпуклую (вверх) оболочку» нашего множества прямых (её еще называют нижней ошибающей множества прямых на плоскости). Назовем ее «&amp;lt;tex&amp;gt;y = convex(x)&amp;lt;/tex&amp;gt;». Видно, что множество точек &amp;lt;math&amp;gt;(x, convex(x))&amp;lt;/math&amp;gt; представляет собой выпуклую вверх функцию.&lt;br /&gt;
&lt;br /&gt;
==Цель нижней огибающей множества прямых==&lt;br /&gt;
Пусть мы считаем динамику для &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева. Его задает &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt;. Итак, нам нужно для данного &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt; найти &amp;lt;tex&amp;gt;\min\limits_{j=0..i-1}(k[j] \cdot x[i] + b[i]) = \min\limits_{j=0..i-1}(y[j](x[i]))&amp;lt;/tex&amp;gt;. Это выражение есть &amp;lt;math&amp;gt;convex(x[i])&amp;lt;/math&amp;gt;. Из монотонности угловых коэффицентов отрезков, задающих выпуклую оболочку, и их расположения по координаты x следует то, что отрезок, который пересекает прямую &amp;lt;tex&amp;gt;x = x[i]&amp;lt;/tex&amp;gt;, можно найти бинарным поиском. Это потребует &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; времени на поиск такого &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;dp[i] = k[j] \cdot x[i] + b[j]&amp;lt;/tex&amp;gt;. Теперь осталось научиться поддерживать множество прямых и быстро добавлять &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-ю прямую после того, как мы посчитали &amp;lt;tex&amp;gt;b[i] = dp[i]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Воспользуемся идеей алгоритма построения выпуклой оболочки множества точек. Заведем 2 стека &amp;lt;tex&amp;gt;k[]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;b[]&amp;lt;/tex&amp;gt;, которые задают прямые в отсортированном порядке их угловыми коэффицентами и свободными членами. Рассмотрим ситуацию, когда мы хотим добавить новую (&amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-тую) прямую в множество. Пусть сейчас в множестве лежит &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt; прямых (нумерация с 1). Пусть &amp;lt;tex&amp;gt;(xL, yL)&amp;lt;/tex&amp;gt; - точка пересечения &amp;lt;tex&amp;gt;sz - 1&amp;lt;/tex&amp;gt;-й прямой множества и &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-й, а &amp;lt;tex&amp;gt;(xR, yR)&amp;lt;/tex&amp;gt; - точка пересечения новой прямой, которую мы хотим добавить в конец множества и &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-й. Нас будут интересовать только их &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;-овые координаты &amp;lt;tex&amp;gt;xL&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;xR&amp;lt;/tex&amp;gt;, соответственно. Если оказалось, что новая прямая пересекает &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-ю прямую выпуклой оболочки позже, чем &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-я &amp;lt;tex&amp;gt;sz - 1&amp;lt;/tex&amp;gt;-ю, т.е. &amp;lt;tex&amp;gt;(xL \geqslant xR)&amp;lt;/tex&amp;gt;, то &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-ю удалим из нашего множества, иначе - остановимся. Так будем делать, пока либо кол-во прямых в стеке не станет равным 2, либо &amp;lt;tex&amp;gt;xL&amp;lt;/tex&amp;gt; не станет меньше &amp;lt;tex&amp;gt;xR.&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Асимптотика : аналогично обычному алгоритму построения выпуклой оболочки, каждая прямая ровно &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; раз добавится в стек и максимум &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; раз удалится. Значит время работы перестройки выпуклой оболочки займет &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; суммарно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:picture2convexhull.png]]&lt;br /&gt;
[[Файл:picture3convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
{{Теорема&lt;br /&gt;
|id=th1239. &lt;br /&gt;
|statement=Алгоритм построения нижней огибающей множества прямых корректен.&lt;br /&gt;
|proof=Достаточно показать, что последнюю прямую нужно удалить из множества т.и т.т., когда она наша новая прямая пересекает ее в точке с координатой по оси X, меньшей, чем последняя - предпоследнюю.&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;Y(x) = Kx + B&amp;lt;/tex&amp;gt; - уравнение новой прямой,  &amp;lt;tex&amp;gt;y[i](x) = K[i]x + B[i]&amp;lt;/tex&amp;gt; - уравнения прямых множества. Тогда т.к. &amp;lt;tex&amp;gt;K &amp;lt; K[sz]&amp;lt;/tex&amp;gt;, то при &amp;lt;tex&amp;gt;x \in [- \infty; xR]  : y[sz](x) &amp;lt;= Y(x)&amp;lt;/tex&amp;gt;, а т.к. &amp;lt;tex&amp;gt; K[sz] &amp;lt; K[sz - 1]&amp;lt;/tex&amp;gt;, то при &amp;lt;tex&amp;gt;x \in [xL; + \infty]  : y[sz - 1](x) \geqslant y[sz](x)&amp;lt;/tex&amp;gt;. Если &amp;lt;tex&amp;gt;xL &amp;lt; xR&amp;lt;/tex&amp;gt;, то при &amp;lt;tex&amp;gt;x \in [xL; xR]  : y[sz - 1] \geqslant y[sz](x) и Y(x) \geqslant y[sz](x)&amp;lt;/tex&amp;gt;, т.е. на отрезке &amp;lt;tex&amp;gt;[xL; xR]&amp;lt;/tex&amp;gt; прямая номер sz лежит ниже остальных и её нужно оставить в множестве. Если же &amp;lt;tex&amp;gt;xL &amp;gt; xR&amp;lt;/tex&amp;gt;, то она ниже всех на отрезке &amp;lt;tex&amp;gt;[xL; xR] = \varnothing &amp;lt;/tex&amp;gt;, т.е. её можно удалить из множества&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==Детали реализации:==&lt;br /&gt;
Будем хранить 2 массива : &amp;lt;tex&amp;gt;front[]&amp;lt;/tex&amp;gt; - &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;-координаты, начиная с которых прямые совпадают с выпуклой оболочкой (т.е. i-я прямая совпадает с выпуклой оболочкой текущего множества прямых при &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;[front[i]; front[i + 1])&amp;lt;/tex&amp;gt; ) и &amp;lt;tex&amp;gt;st[]&amp;lt;/tex&amp;gt; - номера деревьев, соответствующих прямым (т.е. &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-я прямая множества, где &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;[1; sz]&amp;lt;/tex&amp;gt; соответствует дереву номер &amp;lt;tex&amp;gt;sz[i]&amp;lt;/tex&amp;gt;). Также воспользуемся тем, что &amp;lt;tex&amp;gt;x[i] = a[i]&amp;lt;/tex&amp;gt; возрастают (по условию задачи), а значит мы можем искать первое такое &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;x[i] \geqslant front[j]&amp;lt;/tex&amp;gt; не бинарным поиском, а методом двух указателей за &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; операций суммарно. Также массив front[] можно хранить в целых числах, округляя х-координаты в сторону лежащих правее по оси x до ближайшего целого (*), т.к. на самом деле мы, считая динамику, подставляем в уравнения прямых только целые &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt;, а значит если &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt;-я прямая пересекается с &amp;lt;tex&amp;gt;k+1&amp;lt;/tex&amp;gt;-й в точке &amp;lt;tex&amp;gt;z +&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\alpha&amp;lt;/tex&amp;gt; (z-целое,  &amp;lt;tex&amp;gt;\alpha&amp;lt;/tex&amp;gt;  &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;[0;1)&amp;lt;/tex&amp;gt;), то мы будем подставлять в их уравнения &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt;  или &amp;lt;tex&amp;gt;z + 1&amp;lt;/tex&amp;gt;. Поэтому можно считать, что новая прямая начинает совпадать с выпуклой оболочкой, начиная с &amp;lt;tex&amp;gt;x = z+1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Реализация==&lt;br /&gt;
  '''int''' &amp;lt;tex&amp;gt;\mathtt{ConvexHullTrick}&amp;lt;/tex&amp;gt;('''int''' a[n], '''int''' c[n])&lt;br /&gt;
     st[1] = 1&lt;br /&gt;
     from[1] = -&amp;lt;tex&amp;gt;\infty&amp;lt;/tex&amp;gt;&amp;lt;font color=green&amp;gt;// первая прямая покрывает все x-ы, начиная с -∞ &amp;lt;/font&amp;gt;&lt;br /&gt;
     sz = 1 &amp;lt;font color=green&amp;gt;// текущий размер выпуклой оболочки &amp;lt;/font&amp;gt;&lt;br /&gt;
     pos = 1 &amp;lt;font color=green&amp;gt;// текущая позиция первого такого j, что x[i] \geqslant front[st[j]] &amp;lt;/font &amp;gt;&lt;br /&gt;
     '''for'''  i = 2..n  &lt;br /&gt;
          '''while''' (front[pos] &amp;lt; x[i]) &amp;lt;font color=green&amp;gt;// метод 1 указателя (ищем первое pos, такое что x[i] покрывается &amp;quot;областью действия&amp;quot; st[pos]-той прямой &amp;lt;/font &amp;gt;&lt;br /&gt;
               pos = pos + 1&lt;br /&gt;
          j = st[pos]&lt;br /&gt;
          dp[i] = K[j]&amp;lt;math&amp;gt;\cdot&amp;lt;/math&amp;gt;a[i] + B[j] &lt;br /&gt;
          '''if''' (i &amp;lt; n)   &amp;lt;font color=green&amp;gt;// если у нас добавляется НЕ последняя прямая, то придется пересчитать выпуклую оболочку &amp;lt;/font &amp;gt;&lt;br /&gt;
                K[i] = c[i]  &amp;lt;font color=green&amp;gt;// наши переобозначения переменных &amp;lt;/font &amp;gt;&lt;br /&gt;
                B[i] = dp[i] &amp;lt;font color=green&amp;gt;// наши переобозначения переменных &amp;lt;/font &amp;gt;&lt;br /&gt;
                x = -&amp;lt;tex&amp;gt;\infty&amp;lt;/tex&amp;gt; &lt;br /&gt;
                '''while''' ''true'' &lt;br /&gt;
                      j = st[sz]&lt;br /&gt;
                      x = divide(B[j] - B[i], K[i] - K[j]) &amp;lt;font color=green&amp;gt;// x-координата пересечения с последней прямой оболочки, округленное в нужную сторону (*) &amp;lt;/font &amp;gt;&lt;br /&gt;
                      '''if''' (x &amp;gt; from[sz]) '''break'''  &amp;lt;font color=green&amp;gt;// перестаем удалять последнюю прямую из множества, если новая прямая пересекает ее позже, чем начинается ее &amp;quot;область действия&amp;quot; &amp;lt;/font &amp;gt;&lt;br /&gt;
                      sz = sz - 1&amp;lt;font color=green&amp;gt;// удаляем последнюю прямую, если она лишняя &amp;lt;/font &amp;gt;&lt;br /&gt;
                 st[sz + 1] = i  &lt;br /&gt;
                 from[sz + 1] = x &amp;lt;font color=green&amp;gt;// добавили новую прямую &amp;lt;/font &amp;gt;&lt;br /&gt;
                 sz = sz + 1&lt;br /&gt;
  '''return''' dp[n] &lt;br /&gt;
Здесь функция &amp;lt;tex&amp;gt;\mathtt{divide}&amp;lt;/tex&amp;gt;(a, b) возвращает нужное(*) округление a / b. Приведем её код :&lt;br /&gt;
 '''int''' &amp;lt;tex&amp;gt;\mathtt{divide}&amp;lt;/tex&amp;gt;('''int''' a, '''int''' b)&lt;br /&gt;
        delta = 0&lt;br /&gt;
        '''if''' (a '''mod''' b ≠ 0) delta = 1&lt;br /&gt;
        '''if''' ((a &amp;gt; 0 '''and''' b &amp;gt; 0) '''or''' (a &amp;lt; 0 '''and''' b &amp;lt; 0)) '''return''' [a / b] + delta&lt;br /&gt;
 '''return''' -[|a| / |b|]&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
Такая реализация будет работать за O(n).&lt;br /&gt;
&lt;br /&gt;
==Динамический convex hull trick==&lt;br /&gt;
	Заметим, что условия на прямые, что &amp;lt;tex&amp;gt;k[i]&amp;lt;/tex&amp;gt; возрастает/убывает и &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt; убывает/возрастает выглядят достаточно редкими для большинства задач. Пусть в задаче таких ограничений нет. Первый способ борьбы с этой проблемой - отсортировать входные данные нужным образом, не испортив свойств задачи (пример : задача G c Санкт-Петербургских сборов к РОИ 2016[http://neerc.ifmo.ru/school/camp-2016/problems/20160318a.pdf]).&lt;br /&gt;
&lt;br /&gt;
	Но рассмотрим общий случай. По-прежнему у нас есть выпуклая оболочка прямых, имея которую мы за &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; можем найти   &amp;lt;tex&amp;gt;dp[i]&amp;lt;/tex&amp;gt;, но теперь вставку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-й прямой в оболочку уже нельзя выполнить описанным ранее способом за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt; в среднем. У нас есть выпуклая оболочка, наша прямая пересекает ее, возможно, «отсекая» несколько отрезков выпуклой оболочки в середине (рис. 4 : красная прямая - та, которую мы хотим вставить в наше множество). Более формально : теперь наша новая прямая будет ниже остальных при &amp;lt;tex&amp;gt;x \in [x1; x2]&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;x1, x2 \in R&amp;lt;/tex&amp;gt; - точки пересечения с некоторыми прямыми, причем &amp;lt;tex&amp;gt;x2&amp;lt;/tex&amp;gt; не обязательно равно &amp;lt;tex&amp;gt;+ \infty&amp;lt;/tex&amp;gt;&lt;br /&gt;
[[Файл:picture4convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
Чтобы уметь вставлять прямую в множество будем хранить &amp;lt;math&amp;gt;std::set&amp;lt;/math&amp;gt; (или любой аналог в других языках программирования) пар &amp;lt;tex&amp;gt;(k, st)&amp;lt;/tex&amp;gt; =  &amp;lt;tex&amp;gt;(коэффицент прямой, ее номер в глобальной нумерации)&amp;lt;/tex&amp;gt;. Когда приходит новая прямая, ищем последнюю прямую с меньшим угловым коэффицентом, чем у той прямой, которую мы хотим добавить в множество. Поиск такой прямой занимает &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt;. Начиная с найденной прямой выполняем &amp;quot;старый&amp;quot; алгоритм (удаляем, пока текущая прямая множества бесполезна). И симметричный алгоритм применяем ко всем прямым справа от нашей (удаляем правого соседа нашей прямой, пока она пересекает нас позже, чем своего правого соседа).&lt;br /&gt;
	&lt;br /&gt;
Асимптотика решения составит &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; на каждый из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; запросов «добавить прямую» + &amp;lt;tex&amp;gt;O(n\cdot\log(n))&amp;lt;/tex&amp;gt; суммарно на удаление прямых, т.к. по-прежнему каждая прямая не более одного раза удалится из множества, а каждое удаление из std::set занимает &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; времени. Итого &amp;lt;math&amp;gt;O(n\cdot\log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Альтернативный подход ==&lt;br /&gt;
Другой способ интерпретировать выражение &amp;lt;tex&amp;gt;dp[i] = \min\limits_{j=0...i-1}(c[j] \cdot a[i] + dp[j])&amp;lt;/tex&amp;gt;  заключается в том, что мы будем пытаться свести задачу к стандартной выпуклой оболочке множества точек. Перепишем выражение средующим образом : &amp;lt;tex&amp;gt;dp[j] + a[i] \cdot c[j] = (dp[j], c[j]) \cdot (1, a[i])&amp;lt;/tex&amp;gt;, т.е. запишем как скалярное произведение векторов &amp;lt;tex&amp;gt;v[j] = (dp[j], c[j])&amp;lt;/tex&amp;gt; и &amp;lt;tex &amp;gt;u[i] = (1, a[i])&amp;lt;/tex &amp;gt;. Вектора  &amp;lt;tex &amp;gt;v[j] =  (dp[j], c[j])&amp;lt;/tex&amp;gt; хотелось бы организовать так, чтобы за &amp;lt;tex &amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; находить вектор, максимизирующий выражение &amp;lt;tex&amp;gt;v[j] \cdot u[i]&amp;lt;/tex&amp;gt;. Посмотрим на рис. 5. Заметим интуитивно очевидный факт : красная точка (вектор) &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; не может давать более оптимальное значение &amp;lt;tex&amp;gt;v[j] \cdot u[i]&amp;lt;/tex&amp;gt; одновременно чем обе синие точки. По этой причине нам достаточно оставить выпуклую оболочку векторов &amp;lt;tex&amp;gt;v[j]&amp;lt;/tex&amp;gt;, а ответ на запрос - это поиск &amp;lt;tex&amp;gt;v[j]&amp;lt;/tex&amp;gt;, максимизирующего проекцию на &amp;lt;tex&amp;gt;u[i]&amp;lt;/tex&amp;gt;. Это задача поиска ближайшей точки выпуклого многоугольника (составленного из точек выпуклой оболочки) к заданной прямой (из &amp;lt;tex&amp;gt;(0, 0)&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;(1, a[i])&amp;lt;/tex&amp;gt;).  Ее можно решить за &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; двумя бинарными или одним тернарным поиском&lt;br /&gt;
Асимптотика алгоритма по-прежнему составит &amp;lt;tex&amp;gt;O(n \cdot \log(n))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Файл:picture5convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
Докажем то, что описанный выше алгоритм корректен. Для этого достаточно показать, что если имеются 3 вектора a, b, c, расположенные как на рис. 5, т.е. точка b не лежит на выпуклой оболочке векторовч &amp;lt;tex&amp;gt;0, a, b, c &amp;lt;/tex&amp;gt; : &amp;lt;tex&amp;gt; \Leftrightarrow |[a-b, b-c]| &amp;lt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
{{Теорема&lt;br /&gt;
|id=th12392. &lt;br /&gt;
|statement=Если есть 3 вектора a, b, c, таких что &amp;lt;tex&amp;gt;|[a-b, b-c]| &amp;lt; 0&amp;lt;/tex&amp;gt; то либо (a, u[i]) &amp;lt; (b, u[i]), либо (c, u[i]) &amp;lt; (b, u[i]).&lt;br /&gt;
|proof=&amp;lt;tex&amp;gt;|[a-b, b-c]| &amp;lt; 0 \Leftrightarrow (a_{x} - b_{x})\cdot(b_{y} - c_{y}) &amp;lt; (a_{y} - b_{y}) \cdot (b_{x} - c_{x})&amp;lt;/tex&amp;gt; (*). Предположим (от противного), что &amp;lt;tex&amp;gt;(b, u[i]) &amp;lt; (a, u[i]) \Leftrightarrow b_{x}  + a[i] \cdot b_{y} &amp;lt; c_{x} + a[i] \cdot c_{y} \Leftrightarrow (b_{x} - c_{x}) &amp;lt; a[i] \cdot (c_{y} - b_{y})&amp;lt;/tex&amp;gt; и  &amp;lt;tex&amp;gt;(b, u[i]) &amp;lt; (c, u[i]) \Leftrightarrow b_{x}  + a[i] \cdot b_{y} &amp;lt; a_{x} + a[i] \cdot a_{y} \Leftrightarrow (a_{x} - b_{x}) &amp;gt; a[i] \cdot (b_{y} - a_{y})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Подставим эти неравенства в (*). Получим цепочку неравенств : &amp;lt;tex&amp;gt;a[i] \cdot (a_{y} - b_{y})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (c_{y} - b_{y}) = a[i]&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (b_{y} - a_{y}) \cdot &amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt;(b_{y} - c_{y})&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt; &amp;lt; (a_{x} - b_{x})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (b_{y} - c_{y})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; &amp;lt; (a_{y} - b_{y}) \cdot &amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt;(b_{x} - c_{x})&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;&amp;lt; a[i] \cdot (a_{y} - b_{y})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (c_{y} - b_{y})&amp;lt;/tex&amp;gt;. Получили противоречие : &amp;lt;tex&amp;gt;a[i] \cdot (a_{y} - b_{y}) \cdot (c_{y} - b_{y}) &amp;lt; a[i] \cdot (a_{y} - b_{y}) \cdot (c_{y} - b_{y})&amp;lt;/tex&amp;gt;. Значит предположение неверно, чтд.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Из доказанной теоремы и следует корректность алгоритма.&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
1) http://neerc.ifmo.ru/wiki/index.php?title=Статические_выпуклые_оболочки:_Джарвис,_Грэхем,_Эндрю,_Чен,_QuickHull&lt;br /&gt;
&lt;br /&gt;
2) http://neerc.ifmo.ru/wiki/index.php?title=Динамическое_программирование&lt;br /&gt;
&lt;br /&gt;
[[Категория:Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория: Динамическое программирование]]&lt;br /&gt;
[[Категория: Способы оптимизации методов динамического программирования]]&lt;/div&gt;</summary>
		<author><name>92.255.113.52</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=Convex_hull_trick&amp;diff=60023</id>
		<title>Convex hull trick</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=Convex_hull_trick&amp;diff=60023"/>
				<updated>2017-01-18T16:55:24Z</updated>
		
		<summary type="html">&lt;p&gt;92.255.113.52: /* Альтернативный подход */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Convex hull trick -- один из методов оптимизации динамического программирования [[http://neerc.ifmo.ru/wiki/index.php?title=Динамическое_программирование]], использующий идею выпуклой оболочки. Позволяет улучшить ассимптотику решения некоторых задачь, решемых методом динамического программирования с &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt; до &amp;lt;tex&amp;gt;O(n\cdot\log(n))&amp;lt;/tex&amp;gt;. Техника впервые появилась в 1995 году (задачу на нее предложили в USACO -- национальной олимпиаде США по программированию). Массовую известность получила после IOI (международной олимпиады по программированию для школьников) 2002.&lt;br /&gt;
&lt;br /&gt;
==Пример задачи, решаемой методом convex hull trick==&lt;br /&gt;
Рассмотрим задачу на ДП:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; деревьев с высотами &amp;lt;tex&amp;gt;a_1, a_2, \dots, a_n&amp;lt;/tex&amp;gt; (в метрах). Требуется спилить их все, потратив минимальное количество монет на заправку&lt;br /&gt;
бензопилы. Но пила устроена так, что она может спиливать только по 1 метру от дерева, к которому ее применили. Также после&lt;br /&gt;
срубленного метра (любого дерева) пилу нужно заправлять, платя за  бензин определенной кол-во монет. Причем стоимость &lt;br /&gt;
бензина зависит от срубленных (полностью) деревьев. Если сейчас максимальный индекс срубленного дерева равен &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, то цена заправки &lt;br /&gt;
равна &amp;lt;tex&amp;gt;c_i&amp;lt;/tex&amp;gt;.  Изначально пила заправлена.&lt;br /&gt;
Также известны следующие ограничения : &amp;lt;tex&amp;gt;c_n = 0, a_1 = 1, a_i&amp;lt;/tex&amp;gt; возрастают, &amp;lt;tex&amp;gt;c_i&amp;lt;/tex&amp;gt; убывают. Изначально пила заправлена.&lt;br /&gt;
(убывание и возрастание нестрогие)&lt;br /&gt;
}}&lt;br /&gt;
(Задача H с Санкт-Петербургских сборов к РОИ 2016[http://neerc.ifmo.ru/school/camp-2016/problems/20160318a.pdf])&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
&amp;lt;includeonly&amp;gt;{{#if: {{{neat|}}}|&lt;br /&gt;
&amp;lt;div style=&amp;quot;background-color: #fcfcfc; float:left;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;background-color: #ddd;&amp;quot;&amp;gt;'''Задача:'''&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;border:1px dashed #2f6fab; padding: 8px; font-style: italic;&amp;quot;&amp;gt;{{{definition}}}&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;|&lt;br /&gt;
&amp;lt;table border=&amp;quot;0&amp;quot; width=&amp;quot;100%&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td style=&amp;quot;background-color: #ddd&amp;quot;&amp;gt;'''Задача:'''&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td style=&amp;quot;border:1px dashed #2f6fab; padding: 8px; background-color: #fcfcfc; font-style: italic;&amp;quot;&amp;gt;{{{definition}}}&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;}}&lt;br /&gt;
&amp;lt;/includeonly&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Наивное решение==&lt;br /&gt;
Сначала заметим важный факт : т.к. &amp;lt;tex&amp;gt;c[i]&amp;lt;/tex&amp;gt; убывают (нестрого) и &amp;lt;tex&amp;gt;c[n] = 0&amp;lt;/tex&amp;gt;, то все &amp;lt;tex&amp;gt;c[i]&amp;lt;/tex&amp;gt; неотрицательны.&lt;br /&gt;
Понятно, что нужно затратив минимальную стоимость срубить последнее (&amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;-е) дерево, т.к. после него все деревья можно будет рубить бесплатно (т.к. &amp;lt;tex&amp;gt;c[n] = 0&amp;lt;/tex&amp;gt;). Посчитаем следующую динамику : &amp;lt;tex&amp;gt;dp[i]&amp;lt;/tex&amp;gt; - минимальная стоимость, заплатив которую можно добиться того, что дерево номер &amp;lt;tex&amp;gt;i.&amp;lt;/tex&amp;gt; будет срублено.&lt;br /&gt;
База динамики : &amp;lt;tex&amp;gt;dp[1] = 0&amp;lt;/tex&amp;gt;, т.к. изначально пила заправлена и высота первого дерева равна 1, по условию задачи.&lt;br /&gt;
Переход динамики :  понятно, что выгодно рубить сначала более дорогие и низкие деревья, а потом более высокие и дешевые (док-во этого факта оставляется читателям как несложное упражнение, т.к. эта идея относится скорее к теме жадных алгоритмнов, чем к теме данной статьи). Поэтому перед  &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-м деревом мы обязательно срубили какое-то &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-е, причем &amp;lt;tex&amp;gt;j \leqslant i - 1&amp;lt;/tex&amp;gt;. Поэтому чтобы найти &amp;lt;tex&amp;gt;dp[i]&amp;lt;/tex&amp;gt; нужно перебрать все &amp;lt;tex&amp;gt;1 \leqslant j \leqslant i - 1&amp;lt;/tex&amp;gt; и попытаться использовать ответ для дерева намер &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;. Итак, пусть перед &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-м деревом мы полностью срубили &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-е, причем высота &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева составляет &amp;lt;tex&amp;gt;a[i]&amp;lt;/tex&amp;gt;, а т.к. последнее дерево, которое мы срубили имеет индекс &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, то стоимость каждого метра &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева составит &amp;lt;tex&amp;gt;c[j]&amp;lt;/tex&amp;gt;.  Поэтому на сруб &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева мы потратим &amp;lt;tex&amp;gt;a[i] \cdot c[j]&amp;lt;/tex&amp;gt; монет. Также не стоит забывать, ситуацию, когда  &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-е дерево полностью срублено, мы получили не бесплатно, а за &amp;lt;tex&amp;gt;dp[j]&amp;lt;/tex&amp;gt; монет.&lt;br /&gt;
Итогвая формула пересчета : &amp;lt;tex&amp;gt;dp[i] = \min\limits_{j=1...i-1} (dp[j] + a[i] \cdot c[j])&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Посмотрим на код выше описанного решения:&lt;br /&gt;
 '''int''' &amp;lt;tex&amp;gt;\mathtt{simpleDP}&amp;lt;/tex&amp;gt;('''int''' a[n], '''int''' c[n])&lt;br /&gt;
    dp[1] = 0&lt;br /&gt;
    dp[2] = dp[3] = ... = dp[n] = &amp;lt;tex&amp;gt;\infty&amp;lt;/tex&amp;gt;&lt;br /&gt;
    '''for'''  i = 1..n-1  &lt;br /&gt;
       dp[i] = &amp;lt;tex&amp;gt;+\infty&amp;lt;/tex&amp;gt;&lt;br /&gt;
       '''for''' j = 0..i-1 &lt;br /&gt;
           '''if''' (dp[j] + a[i] &amp;lt;tex&amp;gt;\cdot&amp;lt;/tex&amp;gt; c[j] &amp;lt; dp[i])&lt;br /&gt;
           dp[i] = dp[j] + a[i] &amp;lt;tex&amp;gt;\cdot&amp;lt;/tex&amp;gt; c[j]&lt;br /&gt;
 '''return''' dp[n]&lt;br /&gt;
Нетрудно видеть, что такая динамика работает за &amp;lt;tex&amp;gt;O(n^2)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Ключевая идея оптимизации==&lt;br /&gt;
Для начала сделаем замену обозначений. Давайте обозначим &amp;lt;tex&amp;gt;dp[j]&amp;lt;/tex&amp;gt; за &amp;lt;tex&amp;gt;b[j]&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;a[i]&amp;lt;/tex&amp;gt; за &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt;, а &amp;lt;tex&amp;gt;c[j]&amp;lt;/tex&amp;gt; за &amp;lt;tex&amp;gt;k[j]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь формула приняла вид &amp;lt;tex&amp;gt;dp[i] = \min\limits_{j=0...i-1}(k[j] \cdot x[i] + b[j])&amp;lt;/tex&amp;gt;. Выражение &amp;lt;tex&amp;gt;k[j] \cdot x + b[j]&amp;lt;/tex&amp;gt; - это в точности уравнение прямой вида &amp;lt;tex&amp;gt;y = kx + b&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сопоставим каждому &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, обработанному ранее, прямую &amp;lt;tex&amp;gt;y[j](x) = k[j] \cdot x + b[j]&amp;lt;/tex&amp;gt;. Из условия «&amp;lt;tex&amp;gt;c[i]&amp;lt;/tex&amp;gt; убывают &amp;lt;tex&amp;gt;\Leftrightarrow  k[j]&amp;lt;/tex&amp;gt; уменьшаются с номером &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;» следует то, что прямые, полученные ранее отсортированы в порядке убывания углового коэффициент. Давайте нарисуем несколько таких прямых :&lt;br /&gt;
&lt;br /&gt;
[[Файл:picture1convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
Выделим множество точек &amp;lt;tex&amp;gt;(x0, y0)&amp;lt;/tex&amp;gt; , таких что все они принадлежат одной из прямых и при этом нету ни одной прямой &amp;lt;tex&amp;gt;y’(x)&amp;lt;/tex&amp;gt;, такой что &amp;lt;tex&amp;gt;y’(x0) &amp;lt; y0&amp;lt;/tex&amp;gt;. Иными словами возьмем «выпуклую (вверх) оболочку» нашего множества прямых (её еще называют нижней ошибающей множества прямых на плоскости). Назовем ее «&amp;lt;tex&amp;gt;y = convex(x)&amp;lt;/tex&amp;gt;». Видно, что множество точек &amp;lt;math&amp;gt;(x, convex(x))&amp;lt;/math&amp;gt; представляет собой выпуклую вверх функцию.&lt;br /&gt;
&lt;br /&gt;
==Цель нижней огибающей множества прямых==&lt;br /&gt;
Пусть мы считаем динамику для &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева. Его задает &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt;. Итак, нам нужно для данного &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt; найти &amp;lt;tex&amp;gt;\min\limits_{j=0..i-1}(k[j] \cdot x[i] + b[i]) = \min\limits_{j=0..i-1}(y[j](x[i]))&amp;lt;/tex&amp;gt;. Это выражение есть &amp;lt;math&amp;gt;convex(x[i])&amp;lt;/math&amp;gt;. Из монотонности угловых коэффицентов отрезков, задающих выпуклую оболочку, и их расположения по координаты x следует то, что отрезок, который пересекает прямую &amp;lt;tex&amp;gt;x = x[i]&amp;lt;/tex&amp;gt;, можно найти бинарным поиском. Это потребует &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; времени на поиск такого &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;dp[i] = k[j] \cdot x[i] + b[j]&amp;lt;/tex&amp;gt;. Теперь осталось научиться поддерживать множество прямых и быстро добавлять &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-ю прямую после того, как мы посчитали &amp;lt;tex&amp;gt;b[i] = dp[i]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Воспользуемся идеей алгоритма построения выпуклой оболочки множества точек. Заведем 2 стека &amp;lt;tex&amp;gt;k[]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;b[]&amp;lt;/tex&amp;gt;, которые задают прямые в отсортированном порядке их угловыми коэффицентами и свободными членами. Рассмотрим ситуацию, когда мы хотим добавить новую (&amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-тую) прямую в множество. Пусть сейчас в множестве лежит &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt; прямых (нумерация с 1). Пусть &amp;lt;tex&amp;gt;(xL, yL)&amp;lt;/tex&amp;gt; - точка пересечения &amp;lt;tex&amp;gt;sz - 1&amp;lt;/tex&amp;gt;-й прямой множества и &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-й, а &amp;lt;tex&amp;gt;(xR, yR)&amp;lt;/tex&amp;gt; - точка пересечения новой прямой, которую мы хотим добавить в конец множества и &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-й. Нас будут интересовать только их &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;-овые координаты &amp;lt;tex&amp;gt;xL&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;xR&amp;lt;/tex&amp;gt;, соответственно. Если оказалось, что новая прямая пересекает &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-ю прямую выпуклой оболочки позже, чем &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-я &amp;lt;tex&amp;gt;sz - 1&amp;lt;/tex&amp;gt;-ю, т.е. &amp;lt;tex&amp;gt;(xL \geqslant xR)&amp;lt;/tex&amp;gt;, то &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-ю удалим из нашего множества, иначе - остановимся. Так будем делать, пока либо кол-во прямых в стеке не станет равным 2, либо &amp;lt;tex&amp;gt;xL&amp;lt;/tex&amp;gt; не станет меньше &amp;lt;tex&amp;gt;xR.&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Асимптотика : аналогично обычному алгоритму построения выпуклой оболочки, каждая прямая ровно &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; раз добавится в стек и максимум &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; раз удалится. Значит время работы перестройки выпуклой оболочки займет &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; суммарно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:picture2convexhull.png]]&lt;br /&gt;
[[Файл:picture3convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
{{Теорема&lt;br /&gt;
|id=th1239. &lt;br /&gt;
|statement=Алгоритм построения нижней огибающей множества прямых корректен.&lt;br /&gt;
|proof=Достаточно показать, что последнюю прямую нужно удалить из множества т.и т.т., когда она наша новая прямая пересекает ее в точке с координатой по оси X, меньшей, чем последняя - предпоследнюю.&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;Y(x) = Kx + B&amp;lt;/tex&amp;gt; - уравнение новой прямой,  &amp;lt;tex&amp;gt;y[i](x) = K[i]x + B[i]&amp;lt;/tex&amp;gt; - уравнения прямых множества. Тогда т.к. &amp;lt;tex&amp;gt;K &amp;lt; K[sz]&amp;lt;/tex&amp;gt;, то при &amp;lt;tex&amp;gt;x \in [- \infty; xR]  : y[sz](x) &amp;lt;= Y(x)&amp;lt;/tex&amp;gt;, а т.к. &amp;lt;tex&amp;gt; K[sz] &amp;lt; K[sz - 1]&amp;lt;/tex&amp;gt;, то при &amp;lt;tex&amp;gt;x \in [xL; + \infty]  : y[sz - 1](x) \geqslant y[sz](x)&amp;lt;/tex&amp;gt;. Если &amp;lt;tex&amp;gt;xL &amp;lt; xR&amp;lt;/tex&amp;gt;, то при &amp;lt;tex&amp;gt;x \in [xL; xR]  : y[sz - 1] \geqslant y[sz](x) и Y(x) \geqslant y[sz](x)&amp;lt;/tex&amp;gt;, т.е. на отрезке &amp;lt;tex&amp;gt;[xL; xR]&amp;lt;/tex&amp;gt; прямая номер sz лежит ниже остальных и её нужно оставить в множестве. Если же &amp;lt;tex&amp;gt;xL &amp;gt; xR&amp;lt;/tex&amp;gt;, то она ниже всех на отрезке &amp;lt;tex&amp;gt;[xL; xR] = \varnothing &amp;lt;/tex&amp;gt;, т.е. её можно удалить из множества&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==Детали реализации:==&lt;br /&gt;
Будем хранить 2 массива : &amp;lt;tex&amp;gt;front[]&amp;lt;/tex&amp;gt; - &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;-координаты, начиная с которых прямые совпадают с выпуклой оболочкой (т.е. i-я прямая совпадает с выпуклой оболочкой текущего множества прямых при &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;[front[i]; front[i + 1])&amp;lt;/tex&amp;gt; ) и &amp;lt;tex&amp;gt;st[]&amp;lt;/tex&amp;gt; - номера деревьев, соответствующих прямым (т.е. &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-я прямая множества, где &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;[1; sz]&amp;lt;/tex&amp;gt; соответствует дереву номер &amp;lt;tex&amp;gt;sz[i]&amp;lt;/tex&amp;gt;). Также воспользуемся тем, что &amp;lt;tex&amp;gt;x[i] = a[i]&amp;lt;/tex&amp;gt; возрастают (по условию задачи), а значит мы можем искать первое такое &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;x[i] \geqslant front[j]&amp;lt;/tex&amp;gt; не бинарным поиском, а методом двух указателей за &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; операций суммарно. Также массив front[] можно хранить в целых числах, округляя х-координаты в сторону лежащих правее по оси x до ближайшего целого (*), т.к. на самом деле мы, считая динамику, подставляем в уравнения прямых только целые &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt;, а значит если &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt;-я прямая пересекается с &amp;lt;tex&amp;gt;k+1&amp;lt;/tex&amp;gt;-й в точке &amp;lt;tex&amp;gt;z +&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\alpha&amp;lt;/tex&amp;gt; (z-целое,  &amp;lt;tex&amp;gt;\alpha&amp;lt;/tex&amp;gt;  &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;[0;1)&amp;lt;/tex&amp;gt;), то мы будем подставлять в их уравнения &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt;  или &amp;lt;tex&amp;gt;z + 1&amp;lt;/tex&amp;gt;. Поэтому можно считать, что новая прямая начинает совпадать с выпуклой оболочкой, начиная с &amp;lt;tex&amp;gt;x = z+1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Реализация==&lt;br /&gt;
  '''int''' &amp;lt;tex&amp;gt;\mathtt{ConvexHullTrick}&amp;lt;/tex&amp;gt;('''int''' a[n], '''int''' c[n])&lt;br /&gt;
     st[1] = 1&lt;br /&gt;
     from[1] = -&amp;lt;tex&amp;gt;\infty&amp;lt;/tex&amp;gt;&amp;lt;font color=green&amp;gt;// первая прямая покрывает все x-ы, начиная с -∞ &amp;lt;/font&amp;gt;&lt;br /&gt;
     sz = 1 &amp;lt;font color=green&amp;gt;// текущий размер выпуклой оболочки &amp;lt;/font&amp;gt;&lt;br /&gt;
     pos = 1 &amp;lt;font color=green&amp;gt;// текущая позиция первого такого j, что x[i] \geqslant front[st[j]] &amp;lt;/font &amp;gt;&lt;br /&gt;
     '''for'''  i = 2..n  &lt;br /&gt;
          '''while''' (front[pos] &amp;lt; x[i]) &amp;lt;font color=green&amp;gt;// метод 1 указателя (ищем первое pos, такое что x[i] покрывается &amp;quot;областью действия&amp;quot; st[pos]-той прямой &amp;lt;/font &amp;gt;&lt;br /&gt;
               pos = pos + 1&lt;br /&gt;
          j = st[pos]&lt;br /&gt;
          dp[i] = K[j]&amp;lt;math&amp;gt;\cdot&amp;lt;/math&amp;gt;a[i] + B[j] &lt;br /&gt;
          '''if''' (i &amp;lt; n)   &amp;lt;font color=green&amp;gt;// если у нас добавляется НЕ последняя прямая, то придется пересчитать выпуклую оболочку &amp;lt;/font &amp;gt;&lt;br /&gt;
                K[i] = c[i]  &amp;lt;font color=green&amp;gt;// наши переобозначения переменных &amp;lt;/font &amp;gt;&lt;br /&gt;
                B[i] = dp[i] &amp;lt;font color=green&amp;gt;// наши переобозначения переменных &amp;lt;/font &amp;gt;&lt;br /&gt;
                x = -&amp;lt;tex&amp;gt;\infty&amp;lt;/tex&amp;gt; &lt;br /&gt;
                '''while''' ''true'' &lt;br /&gt;
                      j = st[sz]&lt;br /&gt;
                      x = divide(B[j] - B[i], K[i] - K[j]) &amp;lt;font color=green&amp;gt;// x-координата пересечения с последней прямой оболочки, округленное в нужную сторону (*) &amp;lt;/font &amp;gt;&lt;br /&gt;
                      '''if''' (x &amp;gt; from[sz]) '''break'''  &amp;lt;font color=green&amp;gt;// перестаем удалять последнюю прямую из множества, если новая прямая пересекает ее позже, чем начинается ее &amp;quot;область действия&amp;quot; &amp;lt;/font &amp;gt;&lt;br /&gt;
                      sz = sz - 1&amp;lt;font color=green&amp;gt;// удаляем последнюю прямую, если она лишняя &amp;lt;/font &amp;gt;&lt;br /&gt;
                 st[sz + 1] = i  &lt;br /&gt;
                 from[sz + 1] = x &amp;lt;font color=green&amp;gt;// добавили новую прямую &amp;lt;/font &amp;gt;&lt;br /&gt;
                 sz = sz + 1&lt;br /&gt;
  '''return''' dp[n] &lt;br /&gt;
Здесь функция &amp;lt;tex&amp;gt;\mathtt{divide}&amp;lt;/tex&amp;gt;(a, b) возвращает нужное(*) округление a / b. Приведем её код :&lt;br /&gt;
 '''int''' &amp;lt;tex&amp;gt;\mathtt{divide}&amp;lt;/tex&amp;gt;('''int''' a, '''int''' b)&lt;br /&gt;
        delta = 0&lt;br /&gt;
        '''if''' (a '''mod''' b ≠ 0) delta = 1&lt;br /&gt;
        '''if''' ((a &amp;gt; 0 '''and''' b &amp;gt; 0) '''or''' (a &amp;lt; 0 '''and''' b &amp;lt; 0)) '''return''' [a / b] + delta&lt;br /&gt;
 '''return''' -[|a| / |b|]&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
Такая реализация будет работать за O(n).&lt;br /&gt;
&lt;br /&gt;
==Динамический convex hull trick==&lt;br /&gt;
	Заметим, что условия на прямые, что &amp;lt;tex&amp;gt;k[i]&amp;lt;/tex&amp;gt; возрастает/убывает и &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt; убывает/возрастает выглядят достаточно редкими для большинства задач. Пусть в задаче таких ограничений нет. Первый способ борьбы с этой проблемой - отсортировать входные данные нужным образом, не испортив свойств задачи (пример : задача G c Санкт-Петербургских сборов к РОИ 2016[http://neerc.ifmo.ru/school/camp-2016/problems/20160318a.pdf]).&lt;br /&gt;
&lt;br /&gt;
	Но рассмотрим общий случай. По-прежнему у нас есть выпуклая оболочка прямых, имея которую мы за &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; можем найти   &amp;lt;tex&amp;gt;dp[i]&amp;lt;/tex&amp;gt;, но теперь вставку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-й прямой в оболочку уже нельзя выполнить описанным ранее способом за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt; в среднем. У нас есть выпуклая оболочка, наша прямая пересекает ее, возможно, «отсекая» несколько отрезков выпуклой оболочки в середине (рис. 4 : красная прямая - та, которую мы хотим вставить в наше множество). Более формально : теперь наша новая прямая будет ниже остальных при &amp;lt;tex&amp;gt;x \in [x1; x2]&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;x1, x2 \in R&amp;lt;/tex&amp;gt; - точки пересечения с некоторыми прямыми, причем &amp;lt;tex&amp;gt;x2&amp;lt;/tex&amp;gt; не обязательно равно &amp;lt;tex&amp;gt;+ \infty&amp;lt;/tex&amp;gt;&lt;br /&gt;
[[Файл:picture4convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
Чтобы уметь вставлять прямую в множество будем хранить &amp;lt;math&amp;gt;std::set&amp;lt;/math&amp;gt; (или любой аналог в других языках программирования) пар &amp;lt;tex&amp;gt;(k, st)&amp;lt;/tex&amp;gt; =  &amp;lt;tex&amp;gt;(коэффицент прямой, ее номер в глобальной нумерации)&amp;lt;/tex&amp;gt;. Когда приходит новая прямая, ищем последнюю прямую с меньшим угловым коэффицентом, чем у той прямой, которую мы хотим добавить в множество. Поиск такой прямой занимает &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt;. Начиная с найденной прямой выполняем &amp;quot;старый&amp;quot; алгоритм (удаляем, пока текущая прямая множества бесполезна). И симметричный алгоритм применяем ко всем прямым справа от нашей (удаляем правого соседа нашей прямой, пока она пересекает нас позже, чем своего правого соседа).&lt;br /&gt;
	&lt;br /&gt;
Асимптотика решения составит &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; на каждый из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; запросов «добавить прямую» + &amp;lt;tex&amp;gt;O(n\cdot\log(n))&amp;lt;/tex&amp;gt; суммарно на удаление прямых, т.к. по-прежнему каждая прямая не более одного раза удалится из множества, а каждое удаление из std::set занимает &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; времени. Итого &amp;lt;math&amp;gt;O(n\cdot\log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Альтернативный подход ==&lt;br /&gt;
Другой способ интерпретировать выражение &amp;lt;tex&amp;gt;dp[i] = \min\limits_{j=0...i-1}(c[j] \cdot a[i] + dp[j])&amp;lt;/tex&amp;gt;  заключается в том, что мы будем пытаться свести задачу к стандартной выпуклой оболочке множества точек. Перепишем выражение средующим образом : &amp;lt;tex&amp;gt;dp[j] + a[i] \cdot c[j] = (dp[j], c[j]) \cdot (1, a[i])&amp;lt;/tex&amp;gt;, т.е. запишем как скалярное произведение векторов &amp;lt;tex&amp;gt;v[j] = (dp[j], c[j])&amp;lt;/tex&amp;gt; и &amp;lt;tex &amp;gt;u[i] = (1, a[i])&amp;lt;/tex &amp;gt;. Вектора  &amp;lt;tex &amp;gt;v[j] =  (dp[j], c[j])&amp;lt;/tex&amp;gt; хотелось бы организовать так, чтобы за &amp;lt;tex &amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; находить вектор, максимизирующий выражение &amp;lt;tex&amp;gt;v[j] \cdot u[i]&amp;lt;/tex&amp;gt;. Посмотрим на рис. 5. Заметим интуитивно очевидный факт : красная точка (вектор) &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; не может давать более оптимальное значение &amp;lt;tex&amp;gt;v[j] \cdot u[i]&amp;lt;/tex&amp;gt; одновременно чем обе синие точки. По этой причине нам достаточно оставить выпуклую оболочку векторов &amp;lt;tex&amp;gt;v[j]&amp;lt;/tex&amp;gt;, а ответ на запрос - это поиск &amp;lt;tex&amp;gt;v[j]&amp;lt;/tex&amp;gt;, максимизирующего проекцию на &amp;lt;tex&amp;gt;u[i]&amp;lt;/tex&amp;gt;. Это задача поиска ближайшей точки выпуклого многоугольника (составленного из точек выпуклой оболочки) к заданной прямой (из &amp;lt;tex&amp;gt;(0, 0)&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;(1, a[i])&amp;lt;/tex&amp;gt;).  Ее можно решить за &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; двумя бинарными или одним тернарным поиском&lt;br /&gt;
Асимптотика алгоритма по-прежнему составит &amp;lt;tex&amp;gt;O(n \cdot \log(n))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Файл:picture5convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
Докажем то, что описанный выше алгоритм корректен.&lt;br /&gt;
{{Теорема&lt;br /&gt;
|id=th12392. &lt;br /&gt;
|statement=Если есть 3 вектора a, b, c, расположенные как на рисунке 5 (т.е. &amp;lt;tex&amp;gt;[a-b, b-c] &amp;lt; 0&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;[u, v]&amp;lt;/tex&amp;gt; - модуль векторного произведения векторов &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;), то либо (a, u[i]) &amp;lt; (b, u[i]), либо (c, u[i]) &amp;lt; (b, u[i]).&lt;br /&gt;
|proof=Распишем условие того, что точка b не лежит на выпуклой оболочке векторов &amp;lt;tex&amp;gt;0, a, b, c &amp;lt;/tex&amp;gt; : &amp;lt;tex&amp;gt;[a-b, b-c] &amp;lt; 0 \Leftrightarrow (a_{x} - b_{x})\cdot(b_{y} - c_{y}) &amp;lt; (a_{y} - b_{y}) \cdot (b_{x} - c_{x})&amp;lt;/tex&amp;gt; (*). Предположим (от противного), что &amp;lt;tex&amp;gt;(b, u[i]) &amp;lt; (a, u[i]) \Leftrightarrow b_{x}  + a[i] \cdot b_{y} &amp;lt; c_{x} + a[i] \cdot c_{y} \Leftrightarrow (b_{x} - c_{x}) &amp;lt; a[i] \cdot (c_{y} - b_{y})&amp;lt;/tex&amp;gt; и  &amp;lt;tex&amp;gt;(b, u[i]) &amp;lt; (c, u[i]) \Leftrightarrow b_{x}  + a[i] \cdot b_{y} &amp;lt; a_{x} + a[i] \cdot a_{y} \Leftrightarrow (a_{x} - b_{x}) &amp;gt; a[i] \cdot (b_{y} - a_{y})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Подставим эти неравенства в (*). Получим цепочку неравенств : &amp;lt;tex&amp;gt;a[i] \cdot (a_{y} - b_{y})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (c_{y} - b_{y}) = a[i]&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (b_{y} - a_{y}) \cdot &amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt;(b_{y} - c_{y})&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt; &amp;lt; (a_{x} - b_{x})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (b_{y} - c_{y})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; &amp;lt; (a_{y} - b_{y}) \cdot &amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt;(b_{x} - c_{x})&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;&amp;lt; a[i] \cdot (a_{y} - b_{y})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (c_{y} - b_{y})&amp;lt;/tex&amp;gt;. Получили противоречие : &amp;lt;tex&amp;gt;a[i] \cdot (a_{y} - b_{y}) \cdot (c_{y} - b_{y}) &amp;lt; a[i] \cdot (a_{y} - b_{y}) \cdot (c_{y} - b_{y})&amp;lt;/tex&amp;gt;. Значит предположение неверно, чтд.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Из доказанной теоремы и следует корректность алгоритма.&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
1) http://neerc.ifmo.ru/wiki/index.php?title=Статические_выпуклые_оболочки:_Джарвис,_Грэхем,_Эндрю,_Чен,_QuickHull&lt;br /&gt;
&lt;br /&gt;
2) http://neerc.ifmo.ru/wiki/index.php?title=Динамическое_программирование&lt;br /&gt;
&lt;br /&gt;
[[Категория:Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория: Динамическое программирование]]&lt;br /&gt;
[[Категория: Способы оптимизации методов динамического программирования]]&lt;/div&gt;</summary>
		<author><name>92.255.113.52</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=Convex_hull_trick&amp;diff=60022</id>
		<title>Convex hull trick</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=Convex_hull_trick&amp;diff=60022"/>
				<updated>2017-01-18T16:40:11Z</updated>
		
		<summary type="html">&lt;p&gt;92.255.113.52: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Convex hull trick -- один из методов оптимизации динамического программирования [[http://neerc.ifmo.ru/wiki/index.php?title=Динамическое_программирование]], использующий идею выпуклой оболочки. Позволяет улучшить ассимптотику решения некоторых задачь, решемых методом динамического программирования с &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt; до &amp;lt;tex&amp;gt;O(n\cdot\log(n))&amp;lt;/tex&amp;gt;. Техника впервые появилась в 1995 году (задачу на нее предложили в USACO -- национальной олимпиаде США по программированию). Массовую известность получила после IOI (международной олимпиады по программированию для школьников) 2002.&lt;br /&gt;
&lt;br /&gt;
==Пример задачи, решаемой методом convex hull trick==&lt;br /&gt;
Рассмотрим задачу на ДП:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; деревьев с высотами &amp;lt;tex&amp;gt;a_1, a_2, \dots, a_n&amp;lt;/tex&amp;gt; (в метрах). Требуется спилить их все, потратив минимальное количество монет на заправку&lt;br /&gt;
бензопилы. Но пила устроена так, что она может спиливать только по 1 метру от дерева, к которому ее применили. Также после&lt;br /&gt;
срубленного метра (любого дерева) пилу нужно заправлять, платя за  бензин определенной кол-во монет. Причем стоимость &lt;br /&gt;
бензина зависит от срубленных (полностью) деревьев. Если сейчас максимальный индекс срубленного дерева равен &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, то цена заправки &lt;br /&gt;
равна &amp;lt;tex&amp;gt;c_i&amp;lt;/tex&amp;gt;.  Изначально пила заправлена.&lt;br /&gt;
Также известны следующие ограничения : &amp;lt;tex&amp;gt;c_n = 0, a_1 = 1, a_i&amp;lt;/tex&amp;gt; возрастают, &amp;lt;tex&amp;gt;c_i&amp;lt;/tex&amp;gt; убывают. Изначально пила заправлена.&lt;br /&gt;
(убывание и возрастание нестрогие)&lt;br /&gt;
}}&lt;br /&gt;
(Задача H с Санкт-Петербургских сборов к РОИ 2016[http://neerc.ifmo.ru/school/camp-2016/problems/20160318a.pdf])&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
&amp;lt;includeonly&amp;gt;{{#if: {{{neat|}}}|&lt;br /&gt;
&amp;lt;div style=&amp;quot;background-color: #fcfcfc; float:left;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;background-color: #ddd;&amp;quot;&amp;gt;'''Задача:'''&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;border:1px dashed #2f6fab; padding: 8px; font-style: italic;&amp;quot;&amp;gt;{{{definition}}}&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;|&lt;br /&gt;
&amp;lt;table border=&amp;quot;0&amp;quot; width=&amp;quot;100%&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td style=&amp;quot;background-color: #ddd&amp;quot;&amp;gt;'''Задача:'''&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td style=&amp;quot;border:1px dashed #2f6fab; padding: 8px; background-color: #fcfcfc; font-style: italic;&amp;quot;&amp;gt;{{{definition}}}&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;}}&lt;br /&gt;
&amp;lt;/includeonly&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Наивное решение==&lt;br /&gt;
Сначала заметим важный факт : т.к. &amp;lt;tex&amp;gt;c[i]&amp;lt;/tex&amp;gt; убывают (нестрого) и &amp;lt;tex&amp;gt;c[n] = 0&amp;lt;/tex&amp;gt;, то все &amp;lt;tex&amp;gt;c[i]&amp;lt;/tex&amp;gt; неотрицательны.&lt;br /&gt;
Понятно, что нужно затратив минимальную стоимость срубить последнее (&amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;-е) дерево, т.к. после него все деревья можно будет рубить бесплатно (т.к. &amp;lt;tex&amp;gt;c[n] = 0&amp;lt;/tex&amp;gt;). Посчитаем следующую динамику : &amp;lt;tex&amp;gt;dp[i]&amp;lt;/tex&amp;gt; - минимальная стоимость, заплатив которую можно добиться того, что дерево номер &amp;lt;tex&amp;gt;i.&amp;lt;/tex&amp;gt; будет срублено.&lt;br /&gt;
База динамики : &amp;lt;tex&amp;gt;dp[1] = 0&amp;lt;/tex&amp;gt;, т.к. изначально пила заправлена и высота первого дерева равна 1, по условию задачи.&lt;br /&gt;
Переход динамики :  понятно, что выгодно рубить сначала более дорогие и низкие деревья, а потом более высокие и дешевые (док-во этого факта оставляется читателям как несложное упражнение, т.к. эта идея относится скорее к теме жадных алгоритмнов, чем к теме данной статьи). Поэтому перед  &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-м деревом мы обязательно срубили какое-то &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-е, причем &amp;lt;tex&amp;gt;j \leqslant i - 1&amp;lt;/tex&amp;gt;. Поэтому чтобы найти &amp;lt;tex&amp;gt;dp[i]&amp;lt;/tex&amp;gt; нужно перебрать все &amp;lt;tex&amp;gt;1 \leqslant j \leqslant i - 1&amp;lt;/tex&amp;gt; и попытаться использовать ответ для дерева намер &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;. Итак, пусть перед &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-м деревом мы полностью срубили &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-е, причем высота &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева составляет &amp;lt;tex&amp;gt;a[i]&amp;lt;/tex&amp;gt;, а т.к. последнее дерево, которое мы срубили имеет индекс &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, то стоимость каждого метра &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева составит &amp;lt;tex&amp;gt;c[j]&amp;lt;/tex&amp;gt;.  Поэтому на сруб &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева мы потратим &amp;lt;tex&amp;gt;a[i] \cdot c[j]&amp;lt;/tex&amp;gt; монет. Также не стоит забывать, ситуацию, когда  &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-е дерево полностью срублено, мы получили не бесплатно, а за &amp;lt;tex&amp;gt;dp[j]&amp;lt;/tex&amp;gt; монет.&lt;br /&gt;
Итогвая формула пересчета : &amp;lt;tex&amp;gt;dp[i] = \min\limits_{j=1...i-1} (dp[j] + a[i] \cdot c[j])&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Посмотрим на код выше описанного решения:&lt;br /&gt;
 '''int''' &amp;lt;tex&amp;gt;\mathtt{simpleDP}&amp;lt;/tex&amp;gt;('''int''' a[n], '''int''' c[n])&lt;br /&gt;
    dp[1] = 0&lt;br /&gt;
    dp[2] = dp[3] = ... = dp[n] = &amp;lt;tex&amp;gt;\infty&amp;lt;/tex&amp;gt;&lt;br /&gt;
    '''for'''  i = 1..n-1  &lt;br /&gt;
       dp[i] = &amp;lt;tex&amp;gt;+\infty&amp;lt;/tex&amp;gt;&lt;br /&gt;
       '''for''' j = 0..i-1 &lt;br /&gt;
           '''if''' (dp[j] + a[i] &amp;lt;tex&amp;gt;\cdot&amp;lt;/tex&amp;gt; c[j] &amp;lt; dp[i])&lt;br /&gt;
           dp[i] = dp[j] + a[i] &amp;lt;tex&amp;gt;\cdot&amp;lt;/tex&amp;gt; c[j]&lt;br /&gt;
 '''return''' dp[n]&lt;br /&gt;
Нетрудно видеть, что такая динамика работает за &amp;lt;tex&amp;gt;O(n^2)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Ключевая идея оптимизации==&lt;br /&gt;
Для начала сделаем замену обозначений. Давайте обозначим &amp;lt;tex&amp;gt;dp[j]&amp;lt;/tex&amp;gt; за &amp;lt;tex&amp;gt;b[j]&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;a[i]&amp;lt;/tex&amp;gt; за &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt;, а &amp;lt;tex&amp;gt;c[j]&amp;lt;/tex&amp;gt; за &amp;lt;tex&amp;gt;k[j]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь формула приняла вид &amp;lt;tex&amp;gt;dp[i] = \min\limits_{j=0...i-1}(k[j] \cdot x[i] + b[j])&amp;lt;/tex&amp;gt;. Выражение &amp;lt;tex&amp;gt;k[j] \cdot x + b[j]&amp;lt;/tex&amp;gt; - это в точности уравнение прямой вида &amp;lt;tex&amp;gt;y = kx + b&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сопоставим каждому &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, обработанному ранее, прямую &amp;lt;tex&amp;gt;y[j](x) = k[j] \cdot x + b[j]&amp;lt;/tex&amp;gt;. Из условия «&amp;lt;tex&amp;gt;c[i]&amp;lt;/tex&amp;gt; убывают &amp;lt;tex&amp;gt;\Leftrightarrow  k[j]&amp;lt;/tex&amp;gt; уменьшаются с номером &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;» следует то, что прямые, полученные ранее отсортированы в порядке убывания углового коэффициент. Давайте нарисуем несколько таких прямых :&lt;br /&gt;
&lt;br /&gt;
[[Файл:picture1convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
Выделим множество точек &amp;lt;tex&amp;gt;(x0, y0)&amp;lt;/tex&amp;gt; , таких что все они принадлежат одной из прямых и при этом нету ни одной прямой &amp;lt;tex&amp;gt;y’(x)&amp;lt;/tex&amp;gt;, такой что &amp;lt;tex&amp;gt;y’(x0) &amp;lt; y0&amp;lt;/tex&amp;gt;. Иными словами возьмем «выпуклую (вверх) оболочку» нашего множества прямых (её еще называют нижней ошибающей множества прямых на плоскости). Назовем ее «&amp;lt;tex&amp;gt;y = convex(x)&amp;lt;/tex&amp;gt;». Видно, что множество точек &amp;lt;math&amp;gt;(x, convex(x))&amp;lt;/math&amp;gt; представляет собой выпуклую вверх функцию.&lt;br /&gt;
&lt;br /&gt;
==Цель нижней огибающей множества прямых==&lt;br /&gt;
Пусть мы считаем динамику для &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева. Его задает &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt;. Итак, нам нужно для данного &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt; найти &amp;lt;tex&amp;gt;\min\limits_{j=0..i-1}(k[j] \cdot x[i] + b[i]) = \min\limits_{j=0..i-1}(y[j](x[i]))&amp;lt;/tex&amp;gt;. Это выражение есть &amp;lt;math&amp;gt;convex(x[i])&amp;lt;/math&amp;gt;. Из монотонности угловых коэффицентов отрезков, задающих выпуклую оболочку, и их расположения по координаты x следует то, что отрезок, который пересекает прямую &amp;lt;tex&amp;gt;x = x[i]&amp;lt;/tex&amp;gt;, можно найти бинарным поиском. Это потребует &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; времени на поиск такого &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;dp[i] = k[j] \cdot x[i] + b[j]&amp;lt;/tex&amp;gt;. Теперь осталось научиться поддерживать множество прямых и быстро добавлять &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-ю прямую после того, как мы посчитали &amp;lt;tex&amp;gt;b[i] = dp[i]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Воспользуемся идеей алгоритма построения выпуклой оболочки множества точек. Заведем 2 стека &amp;lt;tex&amp;gt;k[]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;b[]&amp;lt;/tex&amp;gt;, которые задают прямые в отсортированном порядке их угловыми коэффицентами и свободными членами. Рассмотрим ситуацию, когда мы хотим добавить новую (&amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-тую) прямую в множество. Пусть сейчас в множестве лежит &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt; прямых (нумерация с 1). Пусть &amp;lt;tex&amp;gt;(xL, yL)&amp;lt;/tex&amp;gt; - точка пересечения &amp;lt;tex&amp;gt;sz - 1&amp;lt;/tex&amp;gt;-й прямой множества и &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-й, а &amp;lt;tex&amp;gt;(xR, yR)&amp;lt;/tex&amp;gt; - точка пересечения новой прямой, которую мы хотим добавить в конец множества и &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-й. Нас будут интересовать только их &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;-овые координаты &amp;lt;tex&amp;gt;xL&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;xR&amp;lt;/tex&amp;gt;, соответственно. Если оказалось, что новая прямая пересекает &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-ю прямую выпуклой оболочки позже, чем &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-я &amp;lt;tex&amp;gt;sz - 1&amp;lt;/tex&amp;gt;-ю, т.е. &amp;lt;tex&amp;gt;(xL \geqslant xR)&amp;lt;/tex&amp;gt;, то &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-ю удалим из нашего множества, иначе - остановимся. Так будем делать, пока либо кол-во прямых в стеке не станет равным 2, либо &amp;lt;tex&amp;gt;xL&amp;lt;/tex&amp;gt; не станет меньше &amp;lt;tex&amp;gt;xR.&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Асимптотика : аналогично обычному алгоритму построения выпуклой оболочки, каждая прямая ровно &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; раз добавится в стек и максимум &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; раз удалится. Значит время работы перестройки выпуклой оболочки займет &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; суммарно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:picture2convexhull.png]]&lt;br /&gt;
[[Файл:picture3convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
{{Теорема&lt;br /&gt;
|id=th1239. &lt;br /&gt;
|statement=Алгоритм построения нижней огибающей множества прямых корректен.&lt;br /&gt;
|proof=Достаточно показать, что последнюю прямую нужно удалить из множества т.и т.т., когда она наша новая прямая пересекает ее в точке с координатой по оси X, меньшей, чем последняя - предпоследнюю.&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;Y(x) = Kx + B&amp;lt;/tex&amp;gt; - уравнение новой прямой,  &amp;lt;tex&amp;gt;y[i](x) = K[i]x + B[i]&amp;lt;/tex&amp;gt; - уравнения прямых множества. Тогда т.к. &amp;lt;tex&amp;gt;K &amp;lt; K[sz]&amp;lt;/tex&amp;gt;, то при &amp;lt;tex&amp;gt;x \in [- \infty; xR]  : y[sz](x) &amp;lt;= Y(x)&amp;lt;/tex&amp;gt;, а т.к. &amp;lt;tex&amp;gt; K[sz] &amp;lt; K[sz - 1]&amp;lt;/tex&amp;gt;, то при &amp;lt;tex&amp;gt;x \in [xL; + \infty]  : y[sz - 1](x) \geqslant y[sz](x)&amp;lt;/tex&amp;gt;. Если &amp;lt;tex&amp;gt;xL &amp;lt; xR&amp;lt;/tex&amp;gt;, то при &amp;lt;tex&amp;gt;x \in [xL; xR]  : y[sz - 1] \geqslant y[sz](x) и Y(x) \geqslant y[sz](x)&amp;lt;/tex&amp;gt;, т.е. на отрезке &amp;lt;tex&amp;gt;[xL; xR]&amp;lt;/tex&amp;gt; прямая номер sz лежит ниже остальных и её нужно оставить в множестве. Если же &amp;lt;tex&amp;gt;xL &amp;gt; xR&amp;lt;/tex&amp;gt;, то она ниже всех на отрезке &amp;lt;tex&amp;gt;[xL; xR] = \varnothing &amp;lt;/tex&amp;gt;, т.е. её можно удалить из множества&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==Детали реализации:==&lt;br /&gt;
Будем хранить 2 массива : &amp;lt;tex&amp;gt;front[]&amp;lt;/tex&amp;gt; - &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;-координаты, начиная с которых прямые совпадают с выпуклой оболочкой (т.е. i-я прямая совпадает с выпуклой оболочкой текущего множества прямых при &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;[front[i]; front[i + 1])&amp;lt;/tex&amp;gt; ) и &amp;lt;tex&amp;gt;st[]&amp;lt;/tex&amp;gt; - номера деревьев, соответствующих прямым (т.е. &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-я прямая множества, где &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;[1; sz]&amp;lt;/tex&amp;gt; соответствует дереву номер &amp;lt;tex&amp;gt;sz[i]&amp;lt;/tex&amp;gt;). Также воспользуемся тем, что &amp;lt;tex&amp;gt;x[i] = a[i]&amp;lt;/tex&amp;gt; возрастают (по условию задачи), а значит мы можем искать первое такое &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;x[i] \geqslant front[j]&amp;lt;/tex&amp;gt; не бинарным поиском, а методом двух указателей за &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; операций суммарно. Также массив front[] можно хранить в целых числах, округляя х-координаты в сторону лежащих правее по оси x до ближайшего целого (*), т.к. на самом деле мы, считая динамику, подставляем в уравнения прямых только целые &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt;, а значит если &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt;-я прямая пересекается с &amp;lt;tex&amp;gt;k+1&amp;lt;/tex&amp;gt;-й в точке &amp;lt;tex&amp;gt;z +&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\alpha&amp;lt;/tex&amp;gt; (z-целое,  &amp;lt;tex&amp;gt;\alpha&amp;lt;/tex&amp;gt;  &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;[0;1)&amp;lt;/tex&amp;gt;), то мы будем подставлять в их уравнения &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt;  или &amp;lt;tex&amp;gt;z + 1&amp;lt;/tex&amp;gt;. Поэтому можно считать, что новая прямая начинает совпадать с выпуклой оболочкой, начиная с &amp;lt;tex&amp;gt;x = z+1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Реализация==&lt;br /&gt;
  '''int''' &amp;lt;tex&amp;gt;\mathtt{ConvexHullTrick}&amp;lt;/tex&amp;gt;('''int''' a[n], '''int''' c[n])&lt;br /&gt;
     st[1] = 1&lt;br /&gt;
     from[1] = -&amp;lt;tex&amp;gt;\infty&amp;lt;/tex&amp;gt;&amp;lt;font color=green&amp;gt;// первая прямая покрывает все x-ы, начиная с -∞ &amp;lt;/font&amp;gt;&lt;br /&gt;
     sz = 1 &amp;lt;font color=green&amp;gt;// текущий размер выпуклой оболочки &amp;lt;/font&amp;gt;&lt;br /&gt;
     pos = 1 &amp;lt;font color=green&amp;gt;// текущая позиция первого такого j, что x[i] \geqslant front[st[j]] &amp;lt;/font &amp;gt;&lt;br /&gt;
     '''for'''  i = 2..n  &lt;br /&gt;
          '''while''' (front[pos] &amp;lt; x[i]) &amp;lt;font color=green&amp;gt;// метод 1 указателя (ищем первое pos, такое что x[i] покрывается &amp;quot;областью действия&amp;quot; st[pos]-той прямой &amp;lt;/font &amp;gt;&lt;br /&gt;
               pos = pos + 1&lt;br /&gt;
          j = st[pos]&lt;br /&gt;
          dp[i] = K[j]&amp;lt;math&amp;gt;\cdot&amp;lt;/math&amp;gt;a[i] + B[j] &lt;br /&gt;
          '''if''' (i &amp;lt; n)   &amp;lt;font color=green&amp;gt;// если у нас добавляется НЕ последняя прямая, то придется пересчитать выпуклую оболочку &amp;lt;/font &amp;gt;&lt;br /&gt;
                K[i] = c[i]  &amp;lt;font color=green&amp;gt;// наши переобозначения переменных &amp;lt;/font &amp;gt;&lt;br /&gt;
                B[i] = dp[i] &amp;lt;font color=green&amp;gt;// наши переобозначения переменных &amp;lt;/font &amp;gt;&lt;br /&gt;
                x = -&amp;lt;tex&amp;gt;\infty&amp;lt;/tex&amp;gt; &lt;br /&gt;
                '''while''' ''true'' &lt;br /&gt;
                      j = st[sz]&lt;br /&gt;
                      x = divide(B[j] - B[i], K[i] - K[j]) &amp;lt;font color=green&amp;gt;// x-координата пересечения с последней прямой оболочки, округленное в нужную сторону (*) &amp;lt;/font &amp;gt;&lt;br /&gt;
                      '''if''' (x &amp;gt; from[sz]) '''break'''  &amp;lt;font color=green&amp;gt;// перестаем удалять последнюю прямую из множества, если новая прямая пересекает ее позже, чем начинается ее &amp;quot;область действия&amp;quot; &amp;lt;/font &amp;gt;&lt;br /&gt;
                      sz = sz - 1&amp;lt;font color=green&amp;gt;// удаляем последнюю прямую, если она лишняя &amp;lt;/font &amp;gt;&lt;br /&gt;
                 st[sz + 1] = i  &lt;br /&gt;
                 from[sz + 1] = x &amp;lt;font color=green&amp;gt;// добавили новую прямую &amp;lt;/font &amp;gt;&lt;br /&gt;
                 sz = sz + 1&lt;br /&gt;
  '''return''' dp[n] &lt;br /&gt;
Здесь функция &amp;lt;tex&amp;gt;\mathtt{divide}&amp;lt;/tex&amp;gt;(a, b) возвращает нужное(*) округление a / b. Приведем её код :&lt;br /&gt;
 '''int''' &amp;lt;tex&amp;gt;\mathtt{divide}&amp;lt;/tex&amp;gt;('''int''' a, '''int''' b)&lt;br /&gt;
        delta = 0&lt;br /&gt;
        '''if''' (a '''mod''' b ≠ 0) delta = 1&lt;br /&gt;
        '''if''' ((a &amp;gt; 0 '''and''' b &amp;gt; 0) '''or''' (a &amp;lt; 0 '''and''' b &amp;lt; 0)) '''return''' [a / b] + delta&lt;br /&gt;
 '''return''' -[|a| / |b|]&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
Такая реализация будет работать за O(n).&lt;br /&gt;
&lt;br /&gt;
==Динамический convex hull trick==&lt;br /&gt;
	Заметим, что условия на прямые, что &amp;lt;tex&amp;gt;k[i]&amp;lt;/tex&amp;gt; возрастает/убывает и &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt; убывает/возрастает выглядят достаточно редкими для большинства задач. Пусть в задаче таких ограничений нет. Первый способ борьбы с этой проблемой - отсортировать входные данные нужным образом, не испортив свойств задачи (пример : задача G c Санкт-Петербургских сборов к РОИ 2016[http://neerc.ifmo.ru/school/camp-2016/problems/20160318a.pdf]).&lt;br /&gt;
&lt;br /&gt;
	Но рассмотрим общий случай. По-прежнему у нас есть выпуклая оболочка прямых, имея которую мы за &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; можем найти   &amp;lt;tex&amp;gt;dp[i]&amp;lt;/tex&amp;gt;, но теперь вставку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-й прямой в оболочку уже нельзя выполнить описанным ранее способом за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt; в среднем. У нас есть выпуклая оболочка, наша прямая пересекает ее, возможно, «отсекая» несколько отрезков выпуклой оболочки в середине (рис. 4 : красная прямая - та, которую мы хотим вставить в наше множество). Более формально : теперь наша новая прямая будет ниже остальных при &amp;lt;tex&amp;gt;x \in [x1; x2]&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;x1, x2 \in R&amp;lt;/tex&amp;gt; - точки пересечения с некоторыми прямыми, причем &amp;lt;tex&amp;gt;x2&amp;lt;/tex&amp;gt; не обязательно равно &amp;lt;tex&amp;gt;+ \infty&amp;lt;/tex&amp;gt;&lt;br /&gt;
[[Файл:picture4convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
Чтобы уметь вставлять прямую в множество будем хранить &amp;lt;math&amp;gt;std::set&amp;lt;/math&amp;gt; (или любой аналог в других языках программирования) пар &amp;lt;tex&amp;gt;(k, st)&amp;lt;/tex&amp;gt; =  &amp;lt;tex&amp;gt;(коэффицент прямой, ее номер в глобальной нумерации)&amp;lt;/tex&amp;gt;. Когда приходит новая прямая, ищем последнюю прямую с меньшим угловым коэффицентом, чем у той прямой, которую мы хотим добавить в множество. Поиск такой прямой занимает &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt;. Начиная с найденной прямой выполняем &amp;quot;старый&amp;quot; алгоритм (удаляем, пока текущая прямая множества бесполезна). И симметричный алгоритм применяем ко всем прямым справа от нашей (удаляем правого соседа нашей прямой, пока она пересекает нас позже, чем своего правого соседа).&lt;br /&gt;
	&lt;br /&gt;
Асимптотика решения составит &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; на каждый из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; запросов «добавить прямую» + &amp;lt;tex&amp;gt;O(n\cdot\log(n))&amp;lt;/tex&amp;gt; суммарно на удаление прямых, т.к. по-прежнему каждая прямая не более одного раза удалится из множества, а каждое удаление из std::set занимает &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; времени. Итого &amp;lt;math&amp;gt;O(n\cdot\log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Альтернативный подход ==&lt;br /&gt;
Другой способ интерпретировать выражение &amp;lt;tex&amp;gt;dp[i] = \min\limits_{j=0...i-1}(c[j] \cdot a[i] + dp[j])&amp;lt;/tex&amp;gt;  заключается в том, что мы будем пытаться свести задачу к стандартной выпуклой оболочке множества точек. Перепишем выражение средующим образом : &amp;lt;tex&amp;gt;dp[j] + a[i] \cdot c[j] = (dp[j], c[j]) \cdot (1, a[i])&amp;lt;/tex&amp;gt;, т.е. запишем как скалярное произведение векторов &amp;lt;tex&amp;gt;v[j] = (dp[j], c[j])&amp;lt;/tex&amp;gt; и &amp;lt;tex &amp;gt;u[i] = (1, a[i])&amp;lt;/tex &amp;gt;. Вектора  &amp;lt;tex &amp;gt;v[j] =  (dp[j], c[j])&amp;lt;/tex&amp;gt; хотелось бы организовать так, чтобы за &amp;lt;tex &amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; находить вектор, максимизирующий выражение &amp;lt;tex&amp;gt;v[j] \cdot u[i]&amp;lt;/tex&amp;gt;. Посмотрим на рис. 5. Заметим интуитивно очевидный факт : красная точка (вектор) &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; не может давать более оптимальное значение &amp;lt;tex&amp;gt;v[j] \cdot u[i]&amp;lt;/tex&amp;gt; одновременно чем обе синие точки. По этой причине нам достаточно оставить выпуклую оболочку векторов &amp;lt;tex&amp;gt;v[j]&amp;lt;/tex&amp;gt;, а ответ на запрос - это поиск &amp;lt;tex&amp;gt;v[j]&amp;lt;/tex&amp;gt;, максимизирующего проекцию на &amp;lt;tex&amp;gt;u[i]&amp;lt;/tex&amp;gt;. Это задача поиска ближайшей точки выпуклого многоугольника (составленного из точек выпуклой оболочки) к заданной прямой (из &amp;lt;tex&amp;gt;(0, 0)&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;(1, a[i])&amp;lt;/tex&amp;gt;).  Ее можно решить за &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; двумя бинарными или одним тернарным поиском&lt;br /&gt;
Асимптотика алгоритма по-прежнему составит &amp;lt;tex&amp;gt;O(n \cdot \log(n))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Файл:picture5convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
Доказательство :&lt;br /&gt;
необходимо показать, что если есть 3 вектора a, b, c, расположенные как на рисунке 5 (т.е. &amp;lt;tex&amp;gt;[a-b, b-c] &amp;lt; 0&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;[u, v]&amp;lt;/tex&amp;gt; - модуль векторного произведения векторов &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;), то либо (a, u[i]) &amp;lt; (b, u[i]), либо (c, u[i]) &amp;lt; (b, u[i]).&lt;br /&gt;
&lt;br /&gt;
Распишем условие того, что точка b не лежит на выпуклой оболочке векторов &amp;lt;tex&amp;gt;0, a, b, c &amp;lt;/tex&amp;gt; : &amp;lt;tex&amp;gt;[a-b, b-c] &amp;lt; 0 \Leftrightarrow (a_{x} - b_{x})\cdot(b_{y} - c_{y}) &amp;lt; (a_{y} - b_{y}) \cdot (b_{x} - c_{x})&amp;lt;/tex&amp;gt; (*). Предположим (от противного), что &amp;lt;tex&amp;gt;(b, u[i]) &amp;lt; (a, u[i]) \Leftrightarrow b_{x}  + a[i] \cdot b_{y} &amp;lt; c_{x} + a[i] \cdot c_{y} \Leftrightarrow (b_{x} - c_{x}) &amp;lt; a[i] \cdot (c_{y} - b_{y})&amp;lt;/tex&amp;gt; и  &amp;lt;tex&amp;gt;(b, u[i]) &amp;lt; (c, u[i]) \Leftrightarrow b_{x}  + a[i] \cdot b_{y} &amp;lt; a_{x} + a[i] \cdot a_{y} \Leftrightarrow (a_{x} - b_{x}) &amp;gt; a[i] \cdot (b_{y} - a_{y})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Подставим эти неравенства в (*). Получим цепочку неравенств : &amp;lt;tex&amp;gt;a[i] \cdot (a_{y} - b_{y})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (c_{y} - b_{y}) = a[i]&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (b_{y} - a_{y}) \cdot &amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt;(b_{y} - c_{y})&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt; &amp;lt; (a_{x} - b_{x})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (b_{y} - c_{y})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; &amp;lt; (a_{y} - b_{y}) \cdot &amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt;(b_{x} - c_{x})&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;&amp;lt; a[i] \cdot (a_{y} - b_{y})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (c_{y} - b_{y})&amp;lt;/tex&amp;gt;. Получили противоречие : &amp;lt;tex&amp;gt;a[i] \cdot (a_{y} - b_{y}) \cdot (c_{y} - b_{y}) &amp;lt; a[i] \cdot (a_{y} - b_{y}) \cdot (c_{y} - b_{y})&amp;lt;/tex&amp;gt;. Значит предположение неверно, чтд.&lt;br /&gt;
==См. также==&lt;br /&gt;
1) http://neerc.ifmo.ru/wiki/index.php?title=Статические_выпуклые_оболочки:_Джарвис,_Грэхем,_Эндрю,_Чен,_QuickHull&lt;br /&gt;
&lt;br /&gt;
2) http://neerc.ifmo.ru/wiki/index.php?title=Динамическое_программирование&lt;br /&gt;
&lt;br /&gt;
[[Категория:Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория: Динамическое программирование]]&lt;br /&gt;
[[Категория: Способы оптимизации методов динамического программирования]]&lt;/div&gt;</summary>
		<author><name>92.255.113.52</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=Convex_hull_trick&amp;diff=60021</id>
		<title>Convex hull trick</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=Convex_hull_trick&amp;diff=60021"/>
				<updated>2017-01-18T16:32:06Z</updated>
		
		<summary type="html">&lt;p&gt;92.255.113.52: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Convex hull trick -- один из методов оптимизации динамического программирования [[http://neerc.ifmo.ru/wiki/index.php?title=Динамическое_программирование]], использующий идею выпуклой оболочки. Позволяет улучшить ассимптотику решения некоторых задачь, решемых методом динамического программирования с &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt; до &amp;lt;tex&amp;gt;O(n\cdot\log(n))&amp;lt;/tex&amp;gt;. Техника впервые появилась в 1995 году (задачу на нее предложили в USACO -- национальной олимпиаде США по программированию). Массовую известность получила после IOI (международной олимпиады по программированию для школьников) 2002.&lt;br /&gt;
&lt;br /&gt;
==Пример задачи, решаемой методом convex hull trick==&lt;br /&gt;
Рассмотрим задачу на ДП:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; деревьев с высотами &amp;lt;tex&amp;gt;a_1, a_2, \dots, a_n&amp;lt;/tex&amp;gt; (в метрах). Требуется спилить их все, потратив минимальное количество монет на заправку&lt;br /&gt;
бензопилы. Но пила устроена так, что она может спиливать только по 1 метру от дерева, к которому ее применили. Также после&lt;br /&gt;
срубленного метра (любого дерева) пилу нужно заправлять, платя за  бензин определенной кол-во монет. Причем стоимость &lt;br /&gt;
бензина зависит от срубленных (полностью) деревьев. Если сейчас максимальный индекс срубленного дерева равен &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, то цена заправки &lt;br /&gt;
равна &amp;lt;tex&amp;gt;c_i&amp;lt;/tex&amp;gt;.  Изначально пила заправлена.&lt;br /&gt;
Также известны следующие ограничения : &amp;lt;tex&amp;gt;c_n = 0, a_1 = 1, a_i&amp;lt;/tex&amp;gt; возрастают, &amp;lt;tex&amp;gt;c_i&amp;lt;/tex&amp;gt; убывают. Изначально пила заправлена.&lt;br /&gt;
(убывание и возрастание нестрогие)&lt;br /&gt;
}}&lt;br /&gt;
(Задача H с Санкт-Петербургских сборов к РОИ 2016[http://neerc.ifmo.ru/school/camp-2016/problems/20160318a.pdf])&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
&amp;lt;includeonly&amp;gt;{{#if: {{{neat|}}}|&lt;br /&gt;
&amp;lt;div style=&amp;quot;background-color: #fcfcfc; float:left;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;background-color: #ddd;&amp;quot;&amp;gt;'''Задача:'''&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;border:1px dashed #2f6fab; padding: 8px; font-style: italic;&amp;quot;&amp;gt;{{{definition}}}&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;|&lt;br /&gt;
&amp;lt;table border=&amp;quot;0&amp;quot; width=&amp;quot;100%&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td style=&amp;quot;background-color: #ddd&amp;quot;&amp;gt;'''Задача:'''&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td style=&amp;quot;border:1px dashed #2f6fab; padding: 8px; background-color: #fcfcfc; font-style: italic;&amp;quot;&amp;gt;{{{definition}}}&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;}}&lt;br /&gt;
&amp;lt;/includeonly&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Наивное решение==&lt;br /&gt;
Сначала заметим важный факт : т.к. &amp;lt;tex&amp;gt;c[i]&amp;lt;/tex&amp;gt; убывают (нестрого) и &amp;lt;tex&amp;gt;c[n] = 0&amp;lt;/tex&amp;gt;, то все &amp;lt;tex&amp;gt;c[i]&amp;lt;/tex&amp;gt; неотрицательны.&lt;br /&gt;
Понятно, что нужно затратив минимальную стоимость срубить последнее (&amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;-е) дерево, т.к. после него все деревья можно будет рубить бесплатно (т.к. &amp;lt;tex&amp;gt;c[n] = 0&amp;lt;/tex&amp;gt;). Посчитаем следующую динамику : &amp;lt;tex&amp;gt;dp[i]&amp;lt;/tex&amp;gt; - минимальная стоимость, заплатив которую можно добиться того, что дерево номер &amp;lt;tex&amp;gt;i.&amp;lt;/tex&amp;gt; будет срублено.&lt;br /&gt;
База динамики : &amp;lt;tex&amp;gt;dp[1] = 0&amp;lt;/tex&amp;gt;, т.к. изначально пила заправлена и высота первого дерева равна 1, по условию задачи.&lt;br /&gt;
Переход динамики :  понятно, что выгодно рубить сначала более дорогие и низкие деревья, а потом более высокие и дешевые (док-во этого факта оставляется читателям как несложное упражнение, т.к. эта идея относится скорее к теме жадных алгоритмнов, чем к теме данной статьи). Поэтому перед  &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-м деревом мы обязательно срубили какое-то &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-е, причем &amp;lt;tex&amp;gt;j \leqslant i - 1&amp;lt;/tex&amp;gt;. Поэтому чтобы найти &amp;lt;tex&amp;gt;dp[i]&amp;lt;/tex&amp;gt; нужно перебрать все &amp;lt;tex&amp;gt;1 \leqslant j \leqslant i - 1&amp;lt;/tex&amp;gt; и попытаться использовать ответ для дерева намер &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;. Итак, пусть перед &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-м деревом мы полностью срубили &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-е, причем высота &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева составляет &amp;lt;tex&amp;gt;a[i]&amp;lt;/tex&amp;gt;, а т.к. последнее дерево, которое мы срубили имеет индекс &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, то стоимость каждого метра &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева составит &amp;lt;tex&amp;gt;c[j]&amp;lt;/tex&amp;gt;.  Поэтому на сруб &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева мы потратим &amp;lt;tex&amp;gt;a[i] \cdot c[j]&amp;lt;/tex&amp;gt; монет. Также не стоит забывать, ситуацию, когда  &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-е дерево полностью срублено, мы получили не бесплатно, а за &amp;lt;tex&amp;gt;dp[j]&amp;lt;/tex&amp;gt; монет.&lt;br /&gt;
Итогвая формула пересчета : &amp;lt;tex&amp;gt;dp[i] = \min\limits_{j=1...i-1} (dp[j] + a[i] \cdot c[j])&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Посмотрим на код выше описанного решения:&lt;br /&gt;
 '''int''' &amp;lt;tex&amp;gt;\mathtt{simpleDP}&amp;lt;/tex&amp;gt;('''int''' a[n], '''int''' c[n])&lt;br /&gt;
    dp[1] = 0&lt;br /&gt;
    dp[2] = dp[3] = ... = dp[n] = &amp;lt;tex&amp;gt;\infty&amp;lt;/tex&amp;gt;&lt;br /&gt;
    '''for'''  i = 1..n-1  &lt;br /&gt;
       dp[i] = &amp;lt;tex&amp;gt;+\infty&amp;lt;/tex&amp;gt;&lt;br /&gt;
       '''for''' j = 0..i-1 &lt;br /&gt;
           '''if''' (dp[j] + a[i] &amp;lt;tex&amp;gt;\cdot&amp;lt;/tex&amp;gt; c[j] &amp;lt; dp[i])&lt;br /&gt;
           dp[i] = dp[j] + a[i] &amp;lt;tex&amp;gt;\cdot&amp;lt;/tex&amp;gt; c[j]&lt;br /&gt;
 '''return''' dp[n]&lt;br /&gt;
Нетрудно видеть, что такая динамика работает за &amp;lt;tex&amp;gt;O(n^2)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Ключевая идея оптимизации==&lt;br /&gt;
Для начала сделаем замену обозначений. Давайте обозначим &amp;lt;tex&amp;gt;dp[j]&amp;lt;/tex&amp;gt; за &amp;lt;tex&amp;gt;b[j]&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;a[i]&amp;lt;/tex&amp;gt; за &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt;, а &amp;lt;tex&amp;gt;c[j]&amp;lt;/tex&amp;gt; за &amp;lt;tex&amp;gt;k[j]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь формула приняла вид &amp;lt;tex&amp;gt;dp[i] = \min\limits_{j=0...i-1}(k[j] \cdot x[i] + b[j])&amp;lt;/tex&amp;gt;. Выражение &amp;lt;tex&amp;gt;k[j] \cdot x + b[j]&amp;lt;/tex&amp;gt; - это в точности уравнение прямой вида &amp;lt;tex&amp;gt;y = kx + b&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сопоставим каждому &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, обработанному ранее, прямую &amp;lt;tex&amp;gt;y[j](x) = k[j] \cdot x + b[j]&amp;lt;/tex&amp;gt;. Из условия «&amp;lt;tex&amp;gt;c[i]&amp;lt;/tex&amp;gt; убывают &amp;lt;tex&amp;gt;\Leftrightarrow  k[j]&amp;lt;/tex&amp;gt; уменьшаются с номером &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;» следует то, что прямые, полученные ранее отсортированы в порядке убывания углового коэффициент. Давайте нарисуем несколько таких прямых :&lt;br /&gt;
&lt;br /&gt;
[[Файл:picture1convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
Выделим множество точек &amp;lt;tex&amp;gt;(x0, y0)&amp;lt;/tex&amp;gt; , таких что все они принадлежат одной из прямых и при этом нету ни одной прямой &amp;lt;tex&amp;gt;y’(x)&amp;lt;/tex&amp;gt;, такой что &amp;lt;tex&amp;gt;y’(x0) &amp;lt; y0&amp;lt;/tex&amp;gt;. Иными словами возьмем «выпуклую (вверх) оболочку» нашего множества прямых (её еще называют нижней ошибающей множества прямых на плоскости). Назовем ее «&amp;lt;tex&amp;gt;y = convex(x)&amp;lt;/tex&amp;gt;». Видно, что множество точек &amp;lt;math&amp;gt;(x, convex(x))&amp;lt;/math&amp;gt; представляет собой выпуклую вверх функцию.&lt;br /&gt;
&lt;br /&gt;
==Цель нижней огибающей множества прямых==&lt;br /&gt;
Пусть мы считаем динамику для &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева. Его задает &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt;. Итак, нам нужно для данного &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt; найти &amp;lt;tex&amp;gt;\min\limits_{j=0..i-1}(k[j] \cdot x[i] + b[i]) = \min\limits_{j=0..i-1}(y[j](x[i]))&amp;lt;/tex&amp;gt;. Это выражение есть &amp;lt;math&amp;gt;convex(x[i])&amp;lt;/math&amp;gt;. Из монотонности угловых коэффицентов отрезков, задающих выпуклую оболочку, и их расположения по координаты x следует то, что отрезок, который пересекает прямую &amp;lt;tex&amp;gt;x = x[i]&amp;lt;/tex&amp;gt;, можно найти бинарным поиском. Это потребует &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; времени на поиск такого &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;dp[i] = k[j] \cdot x[i] + b[j]&amp;lt;/tex&amp;gt;. Теперь осталось научиться поддерживать множество прямых и быстро добавлять &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-ю прямую после того, как мы посчитали &amp;lt;tex&amp;gt;b[i] = dp[i]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Воспользуемся идеей алгоритма построения выпуклой оболочки множества точек. Заведем 2 стека &amp;lt;tex&amp;gt;k[]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;b[]&amp;lt;/tex&amp;gt;, которые задают прямые в отсортированном порядке их угловыми коэффицентами и свободными членами. Рассмотрим ситуацию, когда мы хотим добавить новую (&amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-тую) прямую в множество. Пусть сейчас в множестве лежит &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt; прямых (нумерация с 1). Пусть &amp;lt;tex&amp;gt;(xL, yL)&amp;lt;/tex&amp;gt; - точка пересечения &amp;lt;tex&amp;gt;sz - 1&amp;lt;/tex&amp;gt;-й прямой множества и &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-й, а &amp;lt;tex&amp;gt;(xR, yR)&amp;lt;/tex&amp;gt; - точка пересечения новой прямой, которую мы хотим добавить в конец множества и &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-й. Нас будут интересовать только их &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;-овые координаты &amp;lt;tex&amp;gt;xL&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;xR&amp;lt;/tex&amp;gt;, соответственно. Если оказалось, что новая прямая пересекает &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-ю прямую выпуклой оболочки позже, чем &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-я &amp;lt;tex&amp;gt;sz - 1&amp;lt;/tex&amp;gt;-ю, т.е. &amp;lt;tex&amp;gt;(xL \geqslant xR)&amp;lt;/tex&amp;gt;, то &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-ю удалим из нашего множества, иначе - остановимся. Так будем делать, пока либо кол-во прямых в стеке не станет равным 2, либо &amp;lt;tex&amp;gt;xL&amp;lt;/tex&amp;gt; не станет меньше &amp;lt;tex&amp;gt;xR.&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Асимптотика : аналогично обычному алгоритму построения выпуклой оболочки, каждая прямая ровно &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; раз добавится в стек и максимум &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; раз удалится. Значит время работы перестройки выпуклой оболочки займет &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; суммарно.&lt;br /&gt;
&lt;br /&gt;
Корректность : достаточно показать, что последнюю прямую нужно удалить из множества т.и т.т., когда она наша новая прямая пересекает ее в точке с координатой по оси X, меньшей, чем последняя - предпоследнюю.&lt;br /&gt;
&lt;br /&gt;
[[Файл:picture2convexhull.png]]&lt;br /&gt;
[[Файл:picture3convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
Доказательство : Пусть &amp;lt;tex&amp;gt;Y(x) = Kx + B&amp;lt;/tex&amp;gt; - уравнение новой прямой,  &amp;lt;tex&amp;gt;y[i](x) = K[i]x + B[i]&amp;lt;/tex&amp;gt; - уравнения прямых множества. Тогда т.к. &amp;lt;tex&amp;gt;K &amp;lt; K[sz]&amp;lt;/tex&amp;gt;, то при &amp;lt;tex&amp;gt;x \in [- \infty; xR]  : y[sz](x) &amp;lt;= Y(x)&amp;lt;/tex&amp;gt;, а т.к. &amp;lt;tex&amp;gt; K[sz] &amp;lt; K[sz - 1]&amp;lt;/tex&amp;gt;, то при &amp;lt;tex&amp;gt;x \in [xL; + \infty]  : y[sz - 1](x) \geqslant y[sz](x)&amp;lt;/tex&amp;gt;. Если &amp;lt;tex&amp;gt;xL &amp;lt; xR&amp;lt;/tex&amp;gt;, то при &amp;lt;tex&amp;gt;x \in [xL; xR]  : y[sz - 1] \geqslant y[sz](x) и Y(x) \geqslant y[sz](x)&amp;lt;/tex&amp;gt;, т.е. на отрезке &amp;lt;tex&amp;gt;[xL; xR]&amp;lt;/tex&amp;gt; прямая номер sz лежит ниже остальных и её нужно оставить в множестве. Если же &amp;lt;tex&amp;gt;xL &amp;gt; xR&amp;lt;/tex&amp;gt;, то она ниже всех на отрезке &amp;lt;tex&amp;gt;[xL; xR] = \varnothing &amp;lt;/tex&amp;gt;, т.е. её можно удалить из множества&lt;br /&gt;
&lt;br /&gt;
==Детали реализации:==&lt;br /&gt;
Будем хранить 2 массива : &amp;lt;tex&amp;gt;front[]&amp;lt;/tex&amp;gt; - &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;-координаты, начиная с которых прямые совпадают с выпуклой оболочкой (т.е. i-я прямая совпадает с выпуклой оболочкой текущего множества прямых при &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;[front[i]; front[i + 1])&amp;lt;/tex&amp;gt; ) и &amp;lt;tex&amp;gt;st[]&amp;lt;/tex&amp;gt; - номера деревьев, соответствующих прямым (т.е. &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-я прямая множества, где &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;[1; sz]&amp;lt;/tex&amp;gt; соответствует дереву номер &amp;lt;tex&amp;gt;sz[i]&amp;lt;/tex&amp;gt;). Также воспользуемся тем, что &amp;lt;tex&amp;gt;x[i] = a[i]&amp;lt;/tex&amp;gt; возрастают (по условию задачи), а значит мы можем искать первое такое &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;x[i] \geqslant front[j]&amp;lt;/tex&amp;gt; не бинарным поиском, а методом двух указателей за &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; операций суммарно. Также массив front[] можно хранить в целых числах, округляя х-координаты в сторону лежащих правее по оси x до ближайшего целого (*), т.к. на самом деле мы, считая динамику, подставляем в уравнения прямых только целые &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt;, а значит если &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt;-я прямая пересекается с &amp;lt;tex&amp;gt;k+1&amp;lt;/tex&amp;gt;-й в точке &amp;lt;tex&amp;gt;z +&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\alpha&amp;lt;/tex&amp;gt; (z-целое,  &amp;lt;tex&amp;gt;\alpha&amp;lt;/tex&amp;gt;  &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;[0;1)&amp;lt;/tex&amp;gt;), то мы будем подставлять в их уравнения &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt;  или &amp;lt;tex&amp;gt;z + 1&amp;lt;/tex&amp;gt;. Поэтому можно считать, что новая прямая начинает совпадать с выпуклой оболочкой, начиная с &amp;lt;tex&amp;gt;x = z+1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Реализация==&lt;br /&gt;
  '''int''' &amp;lt;tex&amp;gt;\mathtt{ConvexHullTrick}&amp;lt;/tex&amp;gt;('''int''' a[n], '''int''' c[n])&lt;br /&gt;
     st[1] = 1&lt;br /&gt;
     from[1] = -&amp;lt;tex&amp;gt;\infty&amp;lt;/tex&amp;gt;&amp;lt;font color=green&amp;gt;// первая прямая покрывает все x-ы, начиная с -∞ &amp;lt;/font&amp;gt;&lt;br /&gt;
     sz = 1 &amp;lt;font color=green&amp;gt;// текущий размер выпуклой оболочки &amp;lt;/font&amp;gt;&lt;br /&gt;
     pos = 1 &amp;lt;font color=green&amp;gt;// текущая позиция первого такого j, что x[i] \geqslant front[st[j]] &amp;lt;/font &amp;gt;&lt;br /&gt;
     '''for'''  i = 2..n  &lt;br /&gt;
          '''while''' (front[pos] &amp;lt; x[i]) &amp;lt;font color=green&amp;gt;// метод 1 указателя (ищем первое pos, такое что x[i] покрывается &amp;quot;областью действия&amp;quot; st[pos]-той прямой &amp;lt;/font &amp;gt;&lt;br /&gt;
               pos = pos + 1&lt;br /&gt;
          j = st[pos]&lt;br /&gt;
          dp[i] = K[j]&amp;lt;math&amp;gt;\cdot&amp;lt;/math&amp;gt;a[i] + B[j] &lt;br /&gt;
          '''if''' (i &amp;lt; n)   &amp;lt;font color=green&amp;gt;// если у нас добавляется НЕ последняя прямая, то придется пересчитать выпуклую оболочку &amp;lt;/font &amp;gt;&lt;br /&gt;
                K[i] = c[i]  &amp;lt;font color=green&amp;gt;// наши переобозначения переменных &amp;lt;/font &amp;gt;&lt;br /&gt;
                B[i] = dp[i] &amp;lt;font color=green&amp;gt;// наши переобозначения переменных &amp;lt;/font &amp;gt;&lt;br /&gt;
                x = -&amp;lt;tex&amp;gt;\infty&amp;lt;/tex&amp;gt; &lt;br /&gt;
                '''while''' (''true'') &lt;br /&gt;
                      j = st[sz]&lt;br /&gt;
                      x = divide(B[j] - B[i], K[i] - K[j]) &amp;lt;font color=green&amp;gt;// x-координата пересечения с последней прямой оболочки, округленное в нужную сторону (*) &amp;lt;/font &amp;gt;&lt;br /&gt;
                      '''if''' (x &amp;gt; from[sz]) '''break'''  &amp;lt;font color=green&amp;gt;// перестаем удалять последнюю прямую из множества, если новая прямая пересекает ее позже, чем начинается ее &amp;quot;область действия&amp;quot; &amp;lt;/font &amp;gt;&lt;br /&gt;
                      sz = sz - 1&amp;lt;font color=green&amp;gt;// удаляем последнюю прямую, если она лишняя &amp;lt;/font &amp;gt;&lt;br /&gt;
                 st[sz + 1] = i  &lt;br /&gt;
                 from[sz + 1] = x &amp;lt;font color=green&amp;gt;// добавили новую прямую &amp;lt;/font &amp;gt;&lt;br /&gt;
                 sz = sz + 1&lt;br /&gt;
  '''return''' dp[n] &lt;br /&gt;
Здесь функция &amp;lt;tex&amp;gt;\mathtt{divide}&amp;lt;/tex&amp;gt;(a, b) возвращает нужное(*) округление a / b. Приведем её код :&lt;br /&gt;
 '''int''' &amp;lt;tex&amp;gt;\mathtt{divide}&amp;lt;/tex&amp;gt;('''int''' a, '''int''' b)&lt;br /&gt;
        delta = 0&lt;br /&gt;
        '''if''' (a '''mod''' b ≠ 0) delta = 1&lt;br /&gt;
        '''if''' ((a &amp;gt; 0 '''and''' b &amp;gt; 0) '''or''' (a &amp;lt; 0 '''and''' b &amp;lt; 0)) '''return''' [a / b] + delta&lt;br /&gt;
 '''return''' -[|a| / |b|]&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
Такая реализация будет работать за O(n).&lt;br /&gt;
&lt;br /&gt;
==Динамический convex hull trick==&lt;br /&gt;
	Заметим, что условия на прямые, что &amp;lt;tex&amp;gt;k[i]&amp;lt;/tex&amp;gt; возрастает/убывает и &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt; убывает/возрастает выглядят достаточно редкими для большинства задач. Пусть в задаче таких ограничений нет. Первый способ борьбы с этой проблемой - отсортировать входные данные нужным образом, не испортив свойств задачи (пример : задача G c Санкт-Петербургских сборов к РОИ 2016[http://neerc.ifmo.ru/school/camp-2016/problems/20160318a.pdf]).&lt;br /&gt;
&lt;br /&gt;
	Но рассмотрим общий случай. По-прежнему у нас есть выпуклая оболочка прямых, имея которую мы за &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; можем найти   &amp;lt;tex&amp;gt;dp[i]&amp;lt;/tex&amp;gt;, но теперь вставку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-й прямой в оболочку уже нельзя выполнить описанным ранее способом за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt; в среднем. У нас есть выпуклая оболочка, наша прямая пересекает ее, возможно, «отсекая» несколько отрезков выпуклой оболочки в середине (рис. 4 : красная прямая - та, которую мы хотим вставить в наше множество). Более формально : теперь наша новая прямая будет ниже остальных при &amp;lt;tex&amp;gt;x \in [x1; x2]&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;x1, x2 \in R&amp;lt;/tex&amp;gt; - точки пересечения с некоторыми прямыми, причем &amp;lt;tex&amp;gt;x2&amp;lt;/tex&amp;gt; не обязательно равно &amp;lt;tex&amp;gt;+ \infty&amp;lt;/tex&amp;gt;&lt;br /&gt;
[[Файл:picture4convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
Чтобы уметь вставлять прямую в множество будем хранить &amp;lt;math&amp;gt;std::set&amp;lt;/math&amp;gt; (или любой аналог в других языках программирования) пар &amp;lt;tex&amp;gt;(k, st)&amp;lt;/tex&amp;gt; =  &amp;lt;tex&amp;gt;(коэффицент прямой, ее номер в глобальной нумерации)&amp;lt;/tex&amp;gt;. Когда приходит новая прямая, ищем последнюю прямую с меньшим угловым коэффицентом, чем у той прямой, которую мы хотим добавить в множество. Поиск такой прямой занимает &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt;. Начиная с найденной прямой выполняем &amp;quot;старый&amp;quot; алгоритм (удаляем, пока текущая прямая множества бесполезна). И симметричный алгоритм применяем ко всем прямым справа от нашей (удаляем правого соседа нашей прямой, пока она пересекает нас позже, чем своего правого соседа).&lt;br /&gt;
	&lt;br /&gt;
Асимптотика решения составит &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; на каждый из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; запросов «добавить прямую» + &amp;lt;tex&amp;gt;O(n\cdot\log(n))&amp;lt;/tex&amp;gt; суммарно на удаление прямых, т.к. по-прежнему каждая прямая не более одного раза удалится из множества, а каждое удаление из std::set занимает &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; времени. Итого &amp;lt;math&amp;gt;O(n\cdot\log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Альтернативный подход ==&lt;br /&gt;
Другой способ интерпретировать выражение &amp;lt;tex&amp;gt;dp[i] = \min\limits_{j=0...i-1}(c[j] \cdot a[i] + dp[j])&amp;lt;/tex&amp;gt;  заключается в том, что мы будем пытаться свести задачу к стандартной выпуклой оболочке множества точек. Перепишем выражение средующим образом : &amp;lt;tex&amp;gt;dp[j] + a[i] \cdot c[j] = (dp[j], c[j]) \cdot (1, a[i])&amp;lt;/tex&amp;gt;, т.е. запишем как скалярное произведение векторов &amp;lt;tex&amp;gt;v[j] = (dp[j], c[j])&amp;lt;/tex&amp;gt; и &amp;lt;tex &amp;gt;u[i] = (1, a[i])&amp;lt;/tex &amp;gt;. Вектора  &amp;lt;tex &amp;gt;v[j] =  (dp[j], c[j])&amp;lt;/tex&amp;gt; хотелось бы организовать так, чтобы за &amp;lt;tex &amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; находить вектор, максимизирующий выражение &amp;lt;tex&amp;gt;v[j] \cdot u[i]&amp;lt;/tex&amp;gt;. Посмотрим на рис. 5. Заметим интуитивно очевидный факт : красная точка (вектор) &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; не может давать более оптимальное значение &amp;lt;tex&amp;gt;v[j] \cdot u[i]&amp;lt;/tex&amp;gt; одновременно чем обе синие точки. По этой причине нам достаточно оставить выпуклую оболочку векторов &amp;lt;tex&amp;gt;v[j]&amp;lt;/tex&amp;gt;, а ответ на запрос - это поиск &amp;lt;tex&amp;gt;v[j]&amp;lt;/tex&amp;gt;, максимизирующего проекцию на &amp;lt;tex&amp;gt;u[i]&amp;lt;/tex&amp;gt;. Это задача поиска ближайшей точки выпуклого многоугольника (составленного из точек выпуклой оболочки) к заданной прямой (из &amp;lt;tex&amp;gt;(0, 0)&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;(1, a[i])&amp;lt;/tex&amp;gt;).  Ее можно решить за &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; двумя бинарными или одним тернарным поиском&lt;br /&gt;
Асимптотика алгоритма по-прежнему составит &amp;lt;tex&amp;gt;O(n \cdot \log(n))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Файл:picture5convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
Доказательство :&lt;br /&gt;
необходимо показать, что если есть 3 вектора a, b, c, расположенные как на рисунке 5 (т.е. &amp;lt;tex&amp;gt;[a-b, b-c] &amp;lt; 0&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;[u, v]&amp;lt;/tex&amp;gt; - модуль векторного произведения векторов &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;), то либо (a, u[i]) &amp;lt; (b, u[i]), либо (c, u[i]) &amp;lt; (b, u[i]).&lt;br /&gt;
&lt;br /&gt;
Распишем условие того, что точка b не лежит на выпуклой оболочке векторов &amp;lt;tex&amp;gt;0, a, b, c &amp;lt;/tex&amp;gt; : &amp;lt;tex&amp;gt;[a-b, b-c] &amp;lt; 0 \Leftrightarrow (a_{x} - b_{x})\cdot(b_{y} - c_{y}) &amp;lt; (a_{y} - b_{y}) \cdot (b_{x} - c_{x})&amp;lt;/tex&amp;gt; (*). Предположим (от противного), что &amp;lt;tex&amp;gt;(b, u[i]) &amp;lt; (a, u[i]) \Leftrightarrow b_{x}  + a[i] \cdot b_{y} &amp;lt; c_{x} + a[i] \cdot c_{y} \Leftrightarrow (b_{x} - c_{x}) &amp;lt; a[i] \cdot (c_{y} - b_{y})&amp;lt;/tex&amp;gt; и  &amp;lt;tex&amp;gt;(b, u[i]) &amp;lt; (c, u[i]) \Leftrightarrow b_{x}  + a[i] \cdot b_{y} &amp;lt; a_{x} + a[i] \cdot a_{y} \Leftrightarrow (a_{x} - b_{x}) &amp;gt; a[i] \cdot (b_{y} - a_{y})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Подставим эти неравенства в (*). Получим цепочку неравенств : &amp;lt;tex&amp;gt;a[i] \cdot (a_{y} - b_{y})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (c_{y} - b_{y}) = a[i]&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (b_{y} - a_{y}) \cdot &amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt;(b_{y} - c_{y})&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt; &amp;lt; (a_{x} - b_{x})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (b_{y} - c_{y})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; &amp;lt; (a_{y} - b_{y}) \cdot &amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt;(b_{x} - c_{x})&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;&amp;lt; a[i] \cdot (a_{y} - b_{y})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (c_{y} - b_{y})&amp;lt;/tex&amp;gt;. Получили противоречие : &amp;lt;tex&amp;gt;a[i] \cdot (a_{y} - b_{y}) \cdot (c_{y} - b_{y}) &amp;lt; a[i] \cdot (a_{y} - b_{y}) \cdot (c_{y} - b_{y})&amp;lt;/tex&amp;gt;. Значит предположение неверно, чтд.&lt;br /&gt;
==См. также==&lt;br /&gt;
1) http://neerc.ifmo.ru/wiki/index.php?title=Статические_выпуклые_оболочки:_Джарвис,_Грэхем,_Эндрю,_Чен,_QuickHull&lt;br /&gt;
&lt;br /&gt;
2) http://neerc.ifmo.ru/wiki/index.php?title=Динамическое_программирование&lt;br /&gt;
&lt;br /&gt;
[[Категория:Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория: Динамическое программирование]]&lt;br /&gt;
[[Категория: Способы оптимизации методов динамического программирования]]&lt;/div&gt;</summary>
		<author><name>92.255.113.52</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=Convex_hull_trick&amp;diff=60000</id>
		<title>Convex hull trick</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=Convex_hull_trick&amp;diff=60000"/>
				<updated>2017-01-17T22:52:37Z</updated>
		
		<summary type="html">&lt;p&gt;92.255.113.52: /* Динамический convex hull trick */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Convex hull переводится с английского как выпуклая оболочка[http://neerc.ifmo.ru/wiki/index.php?title=Статические_выпуклые_оболочки:_Джарвис,_Грэхем,_Эндрю,_Чен,_QuickHull]. Convex hull trick - один из методов оптимизации динамического программирования[http://neerc.ifmo.ru/wiki/index.php?title=Динамическое_программирование], использующий идею выпуклой оболочки. Позволяет улучшить ассимптотику решения некоторых задачь, решемых методом динамического программирования с &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt; до &amp;lt;tex&amp;gt;O(n\cdot\log(n))&amp;lt;/tex&amp;gt;. Техника впервые появилась в 1995 году (задачу на нее предложили в USACO - национальной олимпиаде США по программированию). Массовую известность получила после IOI (международной олимпиады по программированию для школьников) 2002.&lt;br /&gt;
&lt;br /&gt;
==Пример задачи, решаемой методом convex hull trick==&lt;br /&gt;
Рассмотрим задачу на ДП:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; деревьев с высотами &amp;lt;tex&amp;gt;a_1, a_2, \dots, a_n&amp;lt;/tex&amp;gt; (в метрах). Требуется спилить их все, потратив минимальное количество монет на заправку&lt;br /&gt;
бензопилы. Но пила устроена так, что она может спиливать только по 1 метру от дерева, к которому ее применили. Также после&lt;br /&gt;
срубленного метра (любого дерева) пилу нужно заправлять, платя за  бензин определенной кол-во монет. Причем стоимость &lt;br /&gt;
бензина зависит от срубленных (полностью) деревьев. Если сейчас максимальный индекс срубленного дерева равен &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, то цена заправки &lt;br /&gt;
равна &amp;lt;tex&amp;gt;c_i&amp;lt;/tex&amp;gt;.  Изначально пила заправлена.&lt;br /&gt;
Также известны следующие ограничения : &amp;lt;tex&amp;gt;c_n = 0, a_1 = 1, a_i&amp;lt;/tex&amp;gt; возрастают, &amp;lt;tex&amp;gt;c_i&amp;lt;/tex&amp;gt; убывают. Изначально пила заправлена.&lt;br /&gt;
(убывание и возрастание нестрогие)&lt;br /&gt;
}}&lt;br /&gt;
(Задача H с Санкт-Петербургских сборов к РОИ 2016[http://neerc.ifmo.ru/school/camp-2016/problems/20160318a.pdf])&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
&amp;lt;includeonly&amp;gt;{{#if: {{{neat|}}}|&lt;br /&gt;
&amp;lt;div style=&amp;quot;background-color: #fcfcfc; float:left;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;background-color: #ddd;&amp;quot;&amp;gt;'''Задача:'''&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;border:1px dashed #2f6fab; padding: 8px; font-style: italic;&amp;quot;&amp;gt;{{{definition}}}&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;|&lt;br /&gt;
&amp;lt;table border=&amp;quot;0&amp;quot; width=&amp;quot;100%&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td style=&amp;quot;background-color: #ddd&amp;quot;&amp;gt;'''Задача:'''&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td style=&amp;quot;border:1px dashed #2f6fab; padding: 8px; background-color: #fcfcfc; font-style: italic;&amp;quot;&amp;gt;{{{definition}}}&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;}}&lt;br /&gt;
&amp;lt;/includeonly&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Наивное решение==&lt;br /&gt;
Сначала заметим важный факт : т.к. &amp;lt;tex&amp;gt;c[i]&amp;lt;/tex&amp;gt; убывают (нестрого) и &amp;lt;tex&amp;gt;c[n] = 0&amp;lt;/tex&amp;gt;, то все &amp;lt;tex&amp;gt;c[i]&amp;lt;/tex&amp;gt; неотрицательны.&lt;br /&gt;
Понятно, что нужно затратив минимальную стоимость срубить последнее (&amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;-е) дерево, т.к. после него все деревья можно будет рубить бесплатно (т.к. &amp;lt;tex&amp;gt;c[n] = 0&amp;lt;/tex&amp;gt;). Посчитаем следующую динамику : &amp;lt;tex&amp;gt;dp[i]&amp;lt;/tex&amp;gt; - минимальная стоимость, заплатив которую можно добиться того, что дерево номер &amp;lt;tex&amp;gt;i.&amp;lt;/tex&amp;gt; будет срублено.&lt;br /&gt;
База динамики : &amp;lt;tex&amp;gt;dp[1] = 0&amp;lt;/tex&amp;gt;, т.к. изначально пила заправлена и высота первого дерева равна 1, по условию задачи.&lt;br /&gt;
Переход динамики :  понятно, что выгодно рубить сначала более дорогие и низкие деревья, а потом более высокие и дешевые (док-во этого факта оставляется читателям как несложное упражнение, т.к. эта идея относится скорее к теме жадных алгоритмнов, чем к теме данной статьи). Поэтому перед  &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-м деревом мы обязательно срубили какое-то &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-е, причем &amp;lt;tex&amp;gt;j \leqslant i - 1&amp;lt;/tex&amp;gt;. Поэтому чтобы найти &amp;lt;tex&amp;gt;dp[i]&amp;lt;/tex&amp;gt; нужно перебрать все &amp;lt;tex&amp;gt;1 \leqslant j \leqslant i - 1&amp;lt;/tex&amp;gt; и попытаться использовать ответ для дерева намер &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;. Итак, пусть перед &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-м деревом мы полностью срубили &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-е, причем высота &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева составляет &amp;lt;tex&amp;gt;a[i]&amp;lt;/tex&amp;gt;, а т.к. последнее дерево, которое мы срубили имеет индекс &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, то стоимость каждого метра &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева составит &amp;lt;tex&amp;gt;c[j]&amp;lt;/tex&amp;gt;.  Поэтому на сруб &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева мы потратим &amp;lt;tex&amp;gt;a[i] \cdot c[j]&amp;lt;/tex&amp;gt; монет. Также не стоит забывать, ситуацию, когда  &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-е дерево полностью срублено, мы получили не бесплатно, а за &amp;lt;tex&amp;gt;dp[j]&amp;lt;/tex&amp;gt; монет.&lt;br /&gt;
Итогвая формула пересчета : &amp;lt;tex&amp;gt;dp[i] = \min\limits_{j=1...i-1} (dp[j] + a[i] \cdot c[j])&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Посмотрим на код выше описанного решения:&lt;br /&gt;
 '''int''' &amp;lt;tex&amp;gt;\mathtt{simpleDP}&amp;lt;/tex&amp;gt;('''int''' a[], '''int''' c[], '''int''' n)&lt;br /&gt;
 dp[1] = 0&lt;br /&gt;
 dp[2] = dp[3] = ... = dp[n] = &amp;lt;tex&amp;gt;\infty&amp;lt;/tex&amp;gt;&lt;br /&gt;
 '''for'''  i = 1..n-1  &lt;br /&gt;
    dp[i] = &amp;lt;tex&amp;gt;+\infty&amp;lt;/tex&amp;gt;&lt;br /&gt;
    '''for''' j = 0..i-1 &lt;br /&gt;
        '''if''' (dp[j] + a[i] &amp;lt;tex&amp;gt;\cdot&amp;lt;/tex&amp;gt; c[j] &amp;lt; dp[i])&lt;br /&gt;
        dp[i] = dp[j] + a[i] &amp;lt;tex&amp;gt;\cdot&amp;lt;/tex&amp;gt; c[j]&lt;br /&gt;
 '''return''' dp[n]&lt;br /&gt;
Нетрудно видеть, что такая динамика работает за &amp;lt;tex&amp;gt;O(n^2)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Ключевая идея оптимизации==&lt;br /&gt;
Для начала сделаем замену обозначений. Давайте обозначим &amp;lt;tex&amp;gt;dp[j]&amp;lt;/tex&amp;gt; за &amp;lt;tex&amp;gt;b[j]&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;a[i]&amp;lt;/tex&amp;gt; за &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt;, а &amp;lt;tex&amp;gt;c[j]&amp;lt;/tex&amp;gt; за &amp;lt;tex&amp;gt;k[j]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь формула приняла вид &amp;lt;tex&amp;gt;dp[i] = \min\limits_{j=0...i-1}(k[j] \cdot x[i] + b[j])&amp;lt;/tex&amp;gt;. Выражение &amp;lt;tex&amp;gt;k[j] \cdot x + b[j]&amp;lt;/tex&amp;gt; - это в точности уравнение прямой вида &amp;lt;tex&amp;gt;y = kx + b&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сопоставим каждому &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, обработанному ранее, прямую &amp;lt;tex&amp;gt;y[j](x) = k[j] \cdot x + b[j]&amp;lt;/tex&amp;gt;. Из условия «&amp;lt;tex&amp;gt;c[i]&amp;lt;/tex&amp;gt; убывают &amp;lt;tex&amp;gt;\Leftrightarrow  k[j]&amp;lt;/tex&amp;gt; уменьшаются с номером &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;» следует то, что прямые, полученные ранее отсортированы в порядке убывания углового коэффициент. Давайте нарисуем несколько таких прямых :&lt;br /&gt;
&lt;br /&gt;
[[Файл:picture1convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
Выделим множество точек &amp;lt;tex&amp;gt;(x0, y0)&amp;lt;/tex&amp;gt; , таких что все они принадлежат одной из прямых и при этом нету ни одной прямой &amp;lt;tex&amp;gt;y’(x)&amp;lt;/tex&amp;gt;, такой что &amp;lt;tex&amp;gt;y’(x0) &amp;lt; y0&amp;lt;/tex&amp;gt;. Иными словами возьмем «выпуклую (вверх) оболочку» нашего множества прямых (её еще называют нижней ошибающей множества прямых на плоскости). Назовем ее «&amp;lt;tex&amp;gt;y = convex(x)&amp;lt;/tex&amp;gt;». Видно, что множество точек &amp;lt;math&amp;gt;(x, convex(x))&amp;lt;/math&amp;gt; представляет собой выпуклую вверх функцию.&lt;br /&gt;
&lt;br /&gt;
==Цель нижней огибающей множества прямых==&lt;br /&gt;
Пусть мы считаем динамику для &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева. Его задает &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt;. Итак, нам нужно для данного &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt; найти &amp;lt;tex&amp;gt;\min\limits_{j=0..i-1}(k[j] \cdot x[i] + b[i]) = \min\limits_{j=0..i-1}(y[j](x[i]))&amp;lt;/tex&amp;gt;. Это выражение есть &amp;lt;math&amp;gt;convex(x[i])&amp;lt;/math&amp;gt;. Из монотонности угловых коэффицентов отрезков, задающих выпуклую оболочку, и их расположения по координаты x следует то, что отрезок, который пересекает прямую &amp;lt;tex&amp;gt;x = x[i]&amp;lt;/tex&amp;gt;, можно найти бинарным поиском. Это потребует &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; времени на поиск такого &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;dp[i] = k[j] \cdot x[i] + b[j]&amp;lt;/tex&amp;gt;. Теперь осталось научиться поддерживать множество прямых и быстро добавлять &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-ю прямую после того, как мы посчитали &amp;lt;tex&amp;gt;b[i] = dp[i]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Воспользуемся идеей алгоритма построения выпуклой оболочки множества точек. Заведем 2 стека &amp;lt;tex&amp;gt;k[]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;b[]&amp;lt;/tex&amp;gt;, которые задают прямые в отсортированном порядке их угловыми коэффицентами и свободными членами. Рассмотрим ситуацию, когда мы хотим добавить новую (&amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-тую) прямую в множество. Пусть сейчас в множестве лежит &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt; прямых (нумерация с 1). Пусть &amp;lt;tex&amp;gt;(xL, yL)&amp;lt;/tex&amp;gt; - точка пересечения &amp;lt;tex&amp;gt;sz - 1&amp;lt;/tex&amp;gt;-й прямой множества и &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-й, а &amp;lt;tex&amp;gt;(xR, yR)&amp;lt;/tex&amp;gt; - точка пересечения новой прямой, которую мы хотим добавить в конец множества и &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-й. Нас будут интересовать только их &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;-овые координаты &amp;lt;tex&amp;gt;xL&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;xR&amp;lt;/tex&amp;gt;, соответственно. Если оказалось, что новая прямая пересекает &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-ю прямую выпуклой оболочки позже, чем &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-я &amp;lt;tex&amp;gt;sz - 1&amp;lt;/tex&amp;gt;-ю, т.е. &amp;lt;tex&amp;gt;(xL &amp;gt;= xR)&amp;lt;/tex&amp;gt;, то &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-ю удалим из нашего множества, иначе - остановимся. Так будем делать, пока либо кол-во прямых в стеке не станет равным 2, либо &amp;lt;tex&amp;gt;xL&amp;lt;/tex&amp;gt; не станет меньше &amp;lt;tex&amp;gt;xR.&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Асимптотика : аналогично обычному алгоритму построения выпуклой оболочки, каждая прямая ровно &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; раз добавится в стек и максимум &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; раз удалится. Значит время работы перестройки выпуклой оболочки займет &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; суммарно.&lt;br /&gt;
&lt;br /&gt;
Корректность : достаточно показать, что последнюю прямую нужно удалить из множества т.и т.т., когда она наша новая прямая пересекает ее в точке с координатой по оси X, меньшей, чем последняя - предпоследнюю.&lt;br /&gt;
&lt;br /&gt;
[[Файл:picture2convexhull.png]]&lt;br /&gt;
[[Файл:picture3convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
Доказательство : Пусть &amp;lt;tex&amp;gt;Y(x) = Kx + B&amp;lt;/tex&amp;gt; - уравнение новой прямой,  &amp;lt;tex&amp;gt;y[i](x) = K[i]x + B[i]&amp;lt;/tex&amp;gt; - уравнения прямых множества. Тогда т.к. &amp;lt;tex&amp;gt;K &amp;lt; K[sz]&amp;lt;/tex&amp;gt;, то при &amp;lt;tex&amp;gt;x \in [- \infty; xR]  : y[sz](x) &amp;lt;= Y(x)&amp;lt;/tex&amp;gt;, а т.к. &amp;lt;tex&amp;gt; K[sz] &amp;lt; K[sz - 1]&amp;lt;/tex&amp;gt;, то при &amp;lt;tex&amp;gt;x \in [xL; + \infty]  : y[sz - 1](x) &amp;gt;= y[sz](x)&amp;lt;/tex&amp;gt;. Если &amp;lt;tex&amp;gt;xL &amp;lt; xR&amp;lt;/tex&amp;gt;, то при &amp;lt;tex&amp;gt;x \in [xL; xR]  : y[sz - 1] &amp;gt;= y[sz](x) и Y(x) &amp;gt;= y[sz](x)&amp;lt;/tex&amp;gt;, т.е. на отрезке &amp;lt;tex&amp;gt;[xL; xR]&amp;lt;/tex&amp;gt; прямая номер sz лежит ниже остальных и её нужно оставить в множестве. Если же &amp;lt;tex&amp;gt;xL &amp;gt; xR&amp;lt;/tex&amp;gt;, то она ниже всех на отрезке &amp;lt;tex&amp;gt;[xL; xR] = \varnothing &amp;lt;/tex&amp;gt;, т.е. её можно удалить из множества&lt;br /&gt;
&lt;br /&gt;
==Детали реализации:==&lt;br /&gt;
Будем хранить 2 массива : &amp;lt;tex&amp;gt;front[]&amp;lt;/tex&amp;gt; - &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;-координаты, начиная с которых прямые совпадают с выпуклой оболочкой (т.е. i-я прямая совпадает с выпуклой оболочкой текущего множества прямых при &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;[front[i]; front[i + 1])&amp;lt;/tex&amp;gt; ) и &amp;lt;tex&amp;gt;st[]&amp;lt;/tex&amp;gt; - номера деревьев, соответствующих прямым (т.е. &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-я прямая множества, где &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;[1; sz]&amp;lt;/tex&amp;gt; соответствует дереву номер &amp;lt;tex&amp;gt;sz[i]&amp;lt;/tex&amp;gt;). Также воспользуемся тем, что &amp;lt;tex&amp;gt;x[i] = a[i]&amp;lt;/tex&amp;gt; возрастают (по условию задачи), а значит мы можем искать первое такое &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;x[i] &amp;gt;= front[j]&amp;lt;/tex&amp;gt; не бинарным поиском, а методом двух указателей за &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; операций суммарно. Также массив front[] можно хранить в целых числах, округляя х-координаты в сторону лежащих правее по оси x до ближайшего целого (*), т.к. на самом деле мы, считая динамику, подставляем в уравнения прямых только целые &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt;, а значит если &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt;-я прямая пересекается с &amp;lt;tex&amp;gt;k+1&amp;lt;/tex&amp;gt;-й в точке &amp;lt;tex&amp;gt;z +&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\alpha&amp;lt;/tex&amp;gt; (z-целое,  &amp;lt;tex&amp;gt;\alpha&amp;lt;/tex&amp;gt;  &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;[0;1)&amp;lt;/tex&amp;gt;), то мы будем подставлять в их уравнения &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt;  или &amp;lt;tex&amp;gt;z + 1&amp;lt;/tex&amp;gt;. Поэтому можно считать, что новая прямая начинает совпадать с выпуклой оболочкой, начиная с &amp;lt;tex&amp;gt;x = z+1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Реализация==&lt;br /&gt;
  '''int''' &amp;lt;tex&amp;gt;\mathtt{ConvexHullTrick}&amp;lt;/tex&amp;gt;('''int''' a[], '''int''' c[], '''int''' n)&lt;br /&gt;
  st[1] = 1&lt;br /&gt;
  from[1] = -&amp;lt;tex&amp;gt;\infty&amp;lt;/tex&amp;gt;&amp;lt;font color=green&amp;gt;// первая прямая покрывает все x-ы, начиная с -∞ &amp;lt;/font&amp;gt;&lt;br /&gt;
  sz = 1 &amp;lt;font color=green&amp;gt;// текущий размер выпуклой оболочки &amp;lt;/font&amp;gt;&lt;br /&gt;
  pos = 1 &amp;lt;font color=green&amp;gt;// текущая позиция первого такого j, что x[i] &amp;gt;= front[st[j]] &amp;lt;/font &amp;gt;&lt;br /&gt;
  '''for'''  i = 2..n  &lt;br /&gt;
       '''while''' (front[pos] &amp;lt; x[i]) &amp;lt;font color=green&amp;gt;// метод 1 указателя (ищем первое pos, такое что x[i] покрывается &amp;quot;областью действия&amp;quot; st[pos]-той прямой &amp;lt;/font &amp;gt;&lt;br /&gt;
            pos = pos + 1&lt;br /&gt;
       j = st[pos]&lt;br /&gt;
       dp[i] = K[j]&amp;lt;math&amp;gt;\cdot&amp;lt;/math&amp;gt;a[i] + B[j] &lt;br /&gt;
       '''if''' (i &amp;lt; n)   &amp;lt;font color=green&amp;gt;// если у нас добавляется НЕ последняя прямая, то придется пересчитать выпуклую оболочку &amp;lt;/font &amp;gt;&lt;br /&gt;
             K[i] = c[i]  &amp;lt;font color=green&amp;gt;// наши переобозначения переменных &amp;lt;/font &amp;gt;&lt;br /&gt;
             B[i] = dp[i] &amp;lt;font color=green&amp;gt;// наши переобозначения переменных &amp;lt;/font &amp;gt;&lt;br /&gt;
             x = -&amp;lt;tex&amp;gt;\infty&amp;lt;/tex&amp;gt; &lt;br /&gt;
             '''while''' (''true'') &lt;br /&gt;
                   j = st[sz]&lt;br /&gt;
                   x = divide(B[j] - B[i], K[i] - K[j]) &amp;lt;font color=green&amp;gt;// x-координата пересечения с последней прямой оболочки, округленное в нужную сторону (*) &amp;lt;/font &amp;gt;&lt;br /&gt;
                   '''if''' (x &amp;gt; from[sz]) '''break'''  &amp;lt;font color=green&amp;gt;// перестаем удалять последнюю прямую из множества, если новая прямая пересекает ее позже, чем начинается ее &amp;quot;область действия&amp;quot; &amp;lt;/font &amp;gt;&lt;br /&gt;
                   sz = sz - 1&amp;lt;font color=green&amp;gt;// удаляем последнюю прямую, если она лишняя &amp;lt;/font &amp;gt;&lt;br /&gt;
              st[sz + 1] = i  &lt;br /&gt;
              from[sz + 1] = x &amp;lt;font color=green&amp;gt;// добавили новую прямую &amp;lt;/font &amp;gt;&lt;br /&gt;
              sz = sz + 1&lt;br /&gt;
  '''return''' dp[n] &lt;br /&gt;
Здесь функция divide(a, b) возвращает нужное(*) округление a / b. Приведем её код :&lt;br /&gt;
 '''int''' &amp;lt;tex&amp;gt;\mathtt{divide}&amp;lt;/tex&amp;gt;('''int''' a, '''int''' b)&lt;br /&gt;
        delta = 0&lt;br /&gt;
        '''if''' (a '''mod''' b ≠ 0) delta = 1&lt;br /&gt;
        '''if''' ((a &amp;gt; 0 '''and''' b &amp;gt; 0) '''or''' (a &amp;lt; 0 '''and''' b &amp;lt; 0)) '''return''' [a / b] + delta&lt;br /&gt;
 '''return''' -[|a| / |b|]&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
Такая реализация будет работать за O(n).&lt;br /&gt;
&lt;br /&gt;
==Динамический convex hull trick==&lt;br /&gt;
	Заметим, что условия на прямые, что &amp;lt;tex&amp;gt;k[i]&amp;lt;/tex&amp;gt; возрастает/убывает и &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt; убывает/возрастает выглядят достаточно редкими для большинства задач. Пусть в задаче таких ограничений нет. Первый способ борьбы с этой проблемой - отсортировать входные данные нужным образом, не испортив свойств задачи (пример : задача G c Санкт-Петербургских сборов к РОИ 2016[http://neerc.ifmo.ru/school/camp-2016/problems/20160318a.pdf]).&lt;br /&gt;
&lt;br /&gt;
	Но рассмотрим общий случай. По-прежнему у нас есть выпуклая оболочка прямых, имея которую мы за &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; можем найти   &amp;lt;tex&amp;gt;dp[i]&amp;lt;/tex&amp;gt;, но теперь вставку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-й прямой в оболочку уже нельзя выполнить описанным ранее способом за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt; в среднем. У нас есть выпуклая оболочка, наша прямая пересекает ее, возможно, «отсекая» несколько отрезков выпуклой оболочки в середине (рис. 4 : красная прямая - та, которую мы хотим вставить в наше множество). Более формально : теперь наша новая прямая будет ниже остальных при &amp;lt;tex&amp;gt;x \in [x1; x2]&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;x1, x2 \in R&amp;lt;/tex&amp;gt; - точки пересечения с некоторыми прямыми, причем &amp;lt;tex&amp;gt;x2&amp;lt;/tex&amp;gt; не обязательно равно &amp;lt;tex&amp;gt;+ \infty&amp;lt;/tex&amp;gt;&lt;br /&gt;
[[Файл:picture4convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
Чтобы уметь вставлять прямую в множество будем хранить &amp;lt;math&amp;gt;std::set&amp;lt;/math&amp;gt; (или любой аналог в других языках программирования) пар &amp;lt;tex&amp;gt;(k, st)&amp;lt;/tex&amp;gt; =  &amp;lt;tex&amp;gt;(коэффицент прямой, ее номер в глобальной нумерации)&amp;lt;/tex&amp;gt;. Когда приходит новая прямая, ищем последнюю прямую с меньшим угловым коэффицентом, чем у той прямой, которую мы хотим добавить в множество. Поиск такой прямой занимает &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt;. Начиная с найденной прямой выполняем &amp;quot;старый&amp;quot; алгоритм (удаляем, пока текущая прямая множества бесполезна). И симметричный алгоритм применяем ко всем прямым справа от нашей (удаляем правого соседа нашей прямой, пока она пересекает нас позже, чем своего правого соседа).&lt;br /&gt;
	&lt;br /&gt;
Асимптотика решения составит &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; на каждый из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; запросов «добавить прямую» + &amp;lt;tex&amp;gt;O(n\cdot\log(n))&amp;lt;/tex&amp;gt; суммарно на удаление прямых, т.к. по-прежнему каждая прямая не более одного раза удалится из множества, а каждое удаление из std::set занимает &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; времени. Итого &amp;lt;math&amp;gt;O(n\cdot\log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Альтернативный подход ==&lt;br /&gt;
Другой способ интерпретировать выражение &amp;lt;tex&amp;gt;dp[i] = \min\limits_{j=0...i-1}(c[j] \cdot a[i] + dp[j])&amp;lt;/tex&amp;gt;  заключается в том, что мы будем пытаться свести задачу к стандартной выпуклой оболочке множества точек. Перепишем выражение средующим образом : &amp;lt;tex&amp;gt;dp[j] + a[i] \cdot c[j] = (dp[j], c[j]) \cdot (1, a[i])&amp;lt;/tex&amp;gt;, т.е. запишем как скалярное произведение векторов &amp;lt;tex&amp;gt;v[j] = (dp[j], c[j])&amp;lt;/tex&amp;gt; и &amp;lt;tex &amp;gt;u[i] = (1, a[i])&amp;lt;/tex &amp;gt;. Вектора  &amp;lt;tex &amp;gt;v[j] =  (dp[j], c[j])&amp;lt;/tex&amp;gt; хотелось бы организовать так, чтобы за &amp;lt;tex &amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; находить вектор, максимизирующий выражение &amp;lt;tex&amp;gt;v[j] \cdot u[i]&amp;lt;/tex&amp;gt;. Посмотрим на рис. 5. Заметим интуитивно очевидный факт : красная точка (вектор) &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; не может давать более оптимальное значение &amp;lt;tex&amp;gt;v[j] \cdot u[i]&amp;lt;/tex&amp;gt; одновременно чем обе синие точки. По этой причине нам достаточно оставить выпуклую оболочку векторов &amp;lt;tex&amp;gt;v[j]&amp;lt;/tex&amp;gt;, а ответ на запрос - это поиск &amp;lt;tex&amp;gt;v[j]&amp;lt;/tex&amp;gt;, максимизирующего проекцию на &amp;lt;tex&amp;gt;u[i]&amp;lt;/tex&amp;gt;. Это задача поиска ближайшей точки выпуклого многоугольника (составленного из точек выпуклой оболочки) к заданной прямой (из &amp;lt;tex&amp;gt;(0, 0)&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;(1, a[i])&amp;lt;/tex&amp;gt;).  Ее можно решить за &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; двумя бинарными или одним тернарным поиском&lt;br /&gt;
Асимптотика алгоритма по-прежнему составит &amp;lt;tex&amp;gt;O(n \cdot \log(n))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Файл:picture5convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
Доказательство :&lt;br /&gt;
необходимо показать, что если есть 3 вектора a, b, c, расположенные как на рисунке 5 (т.е. &amp;lt;tex&amp;gt;[a-b, b-c] &amp;lt; 0&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;[u, v]&amp;lt;/tex&amp;gt; - модуль векторного произведения векторов &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;), то либо (a, u[i]) &amp;lt; (b, u[i]), либо (c, u[i]) &amp;lt; (b, u[i]).&lt;br /&gt;
&lt;br /&gt;
Распишем условие того, что точка b не лежит на выпуклой оболочке векторов &amp;lt;tex&amp;gt;0, a, b, c &amp;lt;/tex&amp;gt; : &amp;lt;tex&amp;gt;[a-b, b-c] &amp;lt; 0 \Leftrightarrow (a_{x} - b_{x})\cdot(b_{y} - c_{y}) &amp;lt; (a_{y} - b_{y}) \cdot (b_{x} - c_{x})&amp;lt;/tex&amp;gt; (*). Предположим (от противного), что &amp;lt;tex&amp;gt;(b, u[i]) &amp;lt; (a, u[i]) \Leftrightarrow b_{x}  + a[i] \cdot b_{y} &amp;lt; c_{x} + a[i] \cdot c_{y} \Leftrightarrow (b_{x} - c_{x}) &amp;lt; a[i] \cdot (c_{y} - b_{y})&amp;lt;/tex&amp;gt; и  &amp;lt;tex&amp;gt;(b, u[i]) &amp;lt; (c, u[i]) \Leftrightarrow b_{x}  + a[i] \cdot b_{y} &amp;lt; a_{x} + a[i] \cdot a_{y} \Leftrightarrow (a_{x} - b_{x}) &amp;gt; a[i] \cdot (b_{y} - a_{y})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Подставим эти неравенства в (*). Получим цепочку неравенств : &amp;lt;tex&amp;gt;a[i] \cdot (a_{y} - b_{y})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (c_{y} - b_{y}) = a[i]&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (b_{y} - a_{y}) \cdot &amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt;(b_{y} - c_{y})&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt; &amp;lt; (a_{x} - b_{x})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (b_{y} - c_{y})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; &amp;lt; (a_{y} - b_{y}) \cdot &amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt;(b_{x} - c_{x})&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;&amp;lt; a[i] \cdot (a_{y} - b_{y})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (c_{y} - b_{y})&amp;lt;/tex&amp;gt;. Получили противоречие : &amp;lt;tex&amp;gt;a[i] \cdot (a_{y} - b_{y}) \cdot (c_{y} - b_{y}) &amp;lt; a[i] \cdot (a_{y} - b_{y}) \cdot (c_{y} - b_{y})&amp;lt;/tex&amp;gt;. Значит предположение неверно, чтд.&lt;br /&gt;
==См. также==&lt;br /&gt;
1) http://neerc.ifmo.ru/wiki/index.php?title=Статические_выпуклые_оболочки:_Джарвис,_Грэхем,_Эндрю,_Чен,_QuickHull&lt;br /&gt;
&lt;br /&gt;
2) http://neerc.ifmo.ru/wiki/index.php?title=Динамическое_программирование&lt;br /&gt;
&lt;br /&gt;
[[Категория:Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория: Динамическое программирование]]&lt;br /&gt;
[[Категория: Способы оптимизации методов динамического программирования]]&lt;/div&gt;</summary>
		<author><name>92.255.113.52</name></author>	</entry>

	</feed>