Изменения
Нет описания правки
'''Задача о уровне предка''' (англ. "Level Ancestor problem") является задачей о превращении данного корневого подвешенного дерева <tex>T</tex> в структуру данных, которая сможет определить предка любого узла на заданном расстоянии от корня дерева.
{{Задача
|definition = Дано корневое подвешенное дерево <tex>T</tex> c <tex>n</tex> вершинами. Поступают запросы вида <tex>LA(v, k)</tex>, для каждого из которых необходимо
найти предка вершины <tex>v</tex>, который находится на расстоянии <tex>k</tex> от корня дерева <tex>T</tex>.
}}
Этот алгоритм базируется на различных способах [[Heavy-light декомпозиция | декомпозиции дерева]] (выберем heavy-light декомпозицию), из свойств этого разбиения следует,
что подняться на любую высоту из вершины <tex>v</tex> мы можем за время <tex>O(\log n)</tex>.
Данное разбиение можно строить за <tex>O(n)</tex>, что дает нам алгоритм за < <tex>\langle O(n), O(\log n)\rangle</tex> >.
В данном примере поступает запрос LA(v,2), на который алгоритм должен дать ответ h.
== The Macro-Micro-Tree Algorithm ==
В данном разделе мы докажем, что предподсчет предыдущего алгоритма можно улучшить до <tex>O(n)</tex>.
Для начала рассмотрим алгоритм < <tex>\langle O(L\log n + n), O(1)</tex> >, где <tex>L</tex> это количество листьев.
*С помощью обхода в глубину запомним по одному листу в ее поддереве для каждой вершины
*Воспользуемся алгоритмом лестниц, но будем выполнять предподсчет только для листьев.
*Зададим некую функцию <tex>S(n) = \dfrac{1}{4} \log n</tex>
*Посчитаем размер поддерева для каждой вершины с помощью обхода в глубину, после чего удалим все вершины размер поддерева которых меньше чем <tex>S(n)</tex>.
*Забудем на время про удаленные поддеревья, для оставшегося дерева наш алгоритм работает за < <tex>\langle O(\dfrac{n}{S(n)} \log n + n), O(1)\rangle </tex> >. Получаем алгоритм < <tex>\langle O(n), O(1) \rangle </tex> >. Для удаленных поддеревьев же выполним полный предподсчет: таких деревьев не более чем <tex>2^{2S(n)}</tex>, что дает асимптотику предподсчета <tex>O(\sqrt{n} \log^2{n}) = o(n) = O(n)</tex>.
В итоге полученный алгоритм действительно работает за < <tex>\langle O(n), O(1)\rangle </tex> > времени и за <tex>O(n)</tex> памяти.
== Сравнение с наивными реализациями ==
Используя DFS посчитаем глубину каждой вершины дерева (это можно сделать за <tex>O(n)</tex>), после чего можем из вершины <tex>v</tex> подняться до необходимой глубины вершины <tex>k</tex>,
что так же в худшем случае работает за <tex>O(n)</tex>.
Получили алгоритм за < <tex>\langle O(n), O(n)\rangle</tex> > времени и <tex>O(n)</tex> памяти, где время ответа на
запрос можно улучшить до <tex>O(\log n)</tex> c помощью [[Метод двоичного подъёма | предподсчета двоичных подъемов]] ,
но тогда и время предподсчета в наивной реализации (посчитать подъемы для всех вершин) ухудшится до < <tex>\langle O(n \log n),O(\log n)\rangle </tex> > времени и <tex>O(n \log n)</tex> памяти. Также альтернативой данным двум алгоритмам является полный предподсчет всех возможных запросов, что соответственно дает нам асимптотику < <tex>O(n^2), O(1)</tex> > времени и <tex>O(n^2)</tex> памяти.
Таким образом, самым оптимальным из описанных как по времени, так и по памяти является алгоритм Macro-Micro-Tree.