Level Ancestor problem — различия между версиями
Romech (обсуждение | вклад) (→Сравнение с наивными реализациями) |
Romech (обсуждение | вклад) (→Источники информации) |
||
Строка 105: | Строка 105: | ||
*[https://www.cadmo.ethz.ch/education/lectures/HS18/SAADS/reports/5.pdf Level Ancestor problem simplified Cai Qi] | *[https://www.cadmo.ethz.ch/education/lectures/HS18/SAADS/reports/5.pdf Level Ancestor problem simplified Cai Qi] | ||
*[https://en.wikipedia.org/wiki/Level_ancestor_problem Wikipedia: LA] | *[https://en.wikipedia.org/wiki/Level_ancestor_problem Wikipedia: LA] | ||
− | |||
[[Категория: Алгоритмы и структуры данных]] | [[Категория: Алгоритмы и структуры данных]] |
Версия 18:53, 18 мая 2019
Задача о уровне предка — (англ. "Level Ancestor problem") является задачей о превращении данного подвешенного дерева
в структуру данных, которая сможет определить предка любого узла на заданном расстоянии от корня дерева.
Задача: |
Дано подвешенное дерево | c вершинами. Поступают запросы вида , для каждого из которых необходимо найти предка вершины , который находится на расстоянии от корня дерева .
Содержание
Использование Heavy-light декомпозиции
Этот алгоритм базируется на различных способах декомпозиции дерева (выберем heavy-light декомпозицию), из свойств этого разбиения следует, что подняться на любую высоту из вершины мы можем за время . Данное разбиение можно строить за , что дает нам алгоритм за .
В данном примере поступает запрос LA(v,2), на который алгоритм должен дать ответ h.
Алгоритм лестниц
Longest path decomposition
Это декомпозиция дерева, которая разбивает его на множество вершинно-непересекающихся путей, идущих из каждой вершины в ее ребенка с самым глубоким поддеревом. Сделаем ее следующим образом: обойдем дерево с помощью обхода в глубину, пусть мы стоим в вершине
, обойдем всех ее детей, добавив в путь, идущий в самое глубокое поддерево, т.е. в котором находится вершина с самой большой глубиной. Для каждой вершины сохраним номер пути в который она входит.Ladder decomposition
Это улучшение Longest-path декомпозиции, позволяющее, как мы потом докажем, подниматься на любую высоту за
. Выполним его следующим образом: увеличим каждый путь в два раза вверх, для каждого нового пути сохраним все входящие в него вершины, а для каждой вершины сохраним ее номер в пути, в который она входит. Построение обычной longest-path декомпозиции займет у нас времени (обход в глубину), соответственно удлиннение каждого пути ухудшит асимптотику до .После этого посчитаем двоичные подъемы для каждой вершины за
, что соответственно не ухудшит асимптотику.Псевдокод
Пусть после этого нам пришел запрос LA(v, k).
- — -тый двоичный подъем в предка вершины
- — путь, проходящий через данную вершину
- — номер данной вершины на пути
- — возвращает -тую вершину на пути
function LA(int v,int k): int n = h(v); // получаем глубину вершиныn = n - k; // на столько необходимо подняться до ответа i = ; v = p[i][v] // делаем максимально большой прыжок вверх i = n - i; // на столько осталось еще подняться return ladder[way[v]][num[v] - i]; // так как теперь и ответ находятся на одном пути
Доказательство корректности
Рассмотрим путь, на котором лежит вершина
до удвоения. Он длины хотя бы , так как мы точно знаем, что существует вершина потомок , расстояние до которого ровно (это вершина, из которой мы только что пришли). Значит, после удвоения этот путь стал длины хотя бы , причем хотя бы вершин в нем — предки . Это означает, что вершина, которую мы ищем, находится на этом пути (иначе бы мы могли до этого прыгнуть еще на вверх). Так как мы знаем позицию в этом пути, то нужную вершину мы можем найти за .Таким образом, наш алгоритм работает за
времени и за памяти. Методом четырех русских данный метод можно улучшить до с помощью оптимизации предподсчета.The Macro-Micro-Tree Algorithm
В данном разделе мы докажем, что предподсчет предыдущего алгоритма можно улучшить до
. Для начала рассмотрим алгоритм >, где это количество листьев.- С помощью обхода в глубину запомним по одному листу в ее поддереве для каждой вершины
- Воспользуемся алгоритмом лестниц, но будем выполнять предподсчет только для листьев.
Рассмотрим как можно улучшить данный алгоритм:
- Зададим некую функцию
- Посчитаем размер поддерева для каждой вершины с помощью обхода в глубину, после чего удалим все вершины размер поддерева которых меньше чем .
- Забудем на время про удаленные поддеревья, для оставшегося дерева наш алгоритм работает за . Получаем алгоритм . Для удаленных поддеревьев же выполним полный предподсчет: таких деревьев не более чем , что дает асимптотику предподсчета .
В итоге полученный алгоритм действительно работает за
времени и за памяти.Сравнение с наивными реализациями
Используя DFS посчитаем глубину каждой вершины дерева (это можно сделать за предподсчета двоичных подъемов , но тогда и время предподсчета в наивной реализации (посчитать подъемы для всех вершин) ухудшится до времени и памяти. Также альтернативой данным двум алгоритмам является полный предподсчет всех возможных запросов, что соответственно дает нам асимптотику времени и памяти.
), после чего можем из вершины подняться до необходимой глубины вершины , что так же в худшем случае работает за . Получили алгоритм за времени и памяти, где время ответа на запрос можно улучшить до c помощьюСравнение различных асимптотик из данной статьи:
Обычный подъем до нужного уровня | |||
---|---|---|---|
Полный предподсчет | |||
Двоичные подъемы | |||
Декомпозиция | |||
Алгоритм лестниц | |||
Macro-Micro-Tree Algorithm |