Метод двоичного подъёма — различия между версиями
(→Модификация предподсчета за O(n)) |
|||
| Строка 54: | Строка 54: | ||
</code> | </code> | ||
| − | ==Модификация предподсчета за O(n)== | + | ==Модификация предподсчета за O(n) времени и O(n) памяти== |
| + | Воспользуемся идеей Heavy-light декомпозиции, которая разбивает все вершины дерева на пути с одним важным свойством: поднимаясь от любой вершины до корня дерева придется сменить не более log(n) различных путей. | ||
| + | |||
| + | Для каждой вершины будем хранить следующие значения: | ||
| + | 1) расстояние до корня дерева | ||
| + | 2) количество потомков | ||
| + | 3) предок (начало пути, на котором лежит вершина) | ||
| + | 4) номер вершины, в которую выходит ребро из предка, ведущее в нашу вершину. | ||
| + | |||
| + | Все эти значения можно посчитать при построении декомпозиции. | ||
| + | |||
| + | Перейдем к вычислению LCA: | ||
| + | Будем рекурсивно подниматься в направлении корня. Пусть на данной итерации рассматриваем вершины U, V. Сначала сравним вершины, в которые идут ребра из предков этих вершин. Если они совпадают, то, очевидно, или U лежит на пути от V к корню, или V лежит на пути от U к корню. Значит одна из них и является искомой. Выберем ту, чье расстояние до корня минимально. | ||
| + | |||
| + | Иначе нужно приблизить одну из этих вершин к корню, выбрав вместо нее ее предка. Приближать будем на основании того, какая из вершин останется дальше от корня, после приближения. | ||
| + | |||
| + | Очевидно, что в результате придем или в одну и ту же вершину, или одна из вершин окажется на пути ток корня к другой. Тем самым мы найдем LCA. | ||
| + | |||
| + | ==Ассимптотика== | ||
| + | Очевидно, что для реализации алгоритма требуется O(n) памяти. Heavy-light декомпозиция строится за O(n). По свойству heavy-light декомпозиции, на пути от вершины к корню мы сменим не более log(n) путей. Значит время выполнения запроса также O(log(n)). | ||
==См. также== | ==См. также== | ||
Версия 19:28, 7 мая 2016
Метод двоичного подъема — один из самых простых методов для решения задачи LCA в online. Он не использует метод решение задачи RMQ и основан на методе динамического программирования.
Содержание
Описание алгоритма
Как и большинство on-line алгоритмов для решения задачи LCA, этот метод делает сначала препроцессинг, чтобы потом отвечать на запросы.
Препроцессинг
Препроцессинг заключается в том, чтобы посчитать функцию: — номер вершины, в которую мы придем если пройдем из вершины вверх по подвешенному дереву шагов, причем если мы пришли в корень, то мы там и останемся. Для этого сначала обойдем дерево в глубину и для каждой вершины запишем номер ее родителя и глубину вершины в подвешенном дереве . Если — корень, то . Тогда для функции есть рекуррентная формула:
Для того чтобы отвечать на запросы нам нужны будут только те значения , где , ведь при больших значение будет номером корня.
Всего состояний динамики , где — это количество вершин в дереве. Каждое состояние считается за . Поэтому суммарная сложность времени и памяти препроцессинга — .
Ответы на запросы
Ответы на запросы будут происходить за время . Для ответа на запрос заметим сначала, что если , для некоторых и , то . Поэтому если , то пройдем от вершины на шагов вверх, это и будет новое значение и это можно сделать за . Можно записать число в двоичной системе, это представление этого число в виде суммы степеней двоек, и для всех пройти вверх последовательно из вершины в .
Дальше считаем, что .
Если , то ответ на запрос .
А если , то найдем такие вершины и , такие что , — предок , — предок и . Тогда ответом на запрос будет .
Научимся находить эти вершины и . Для этого сначала инициализируем и . Дальше на каждом шаге находим такое максимальное , что . И проходим из вершин и на шагов вверх. Если такого найти нельзя, то значения и , это те самые вершины, которые нам требуется найти, ведь .
Оценим время работы. Заметим, что найденные строго убывают. Во-первых, потому что мы находим на каждом шаге максимальное значение , а во-вторых, два раза подряд мы одно и то же получить не можем, так как тогда получилось бы, что можно пройти шагов, а значит вместо первого , мы бы нашли . А, значит, всего значений , их можно перебирать в порядке убывания. Сложность ответа на запрос .
Псевдокод
function preprocess():
int[] p = dfs(0)
for i = 1 to n
dp[i][0] = p[i]
for j = 1 to log(n)
for i = 1 to n
dp[i][j] = dp[dp[i][j - 1]][j - 1]
int lca(int v, int u):
if d[v] > d[u]
swap(v, u)
for i = log(n) downto 0
if d[u] - d[v]
u = dp[u][i]
if v == u
return v
for i = log(n) downto 0
if dp[v][i] != dp[u][i]
v = dp[v][i]
u = dp[u][i]
return p[v]
Модификация предподсчета за O(n) времени и O(n) памяти
Воспользуемся идеей Heavy-light декомпозиции, которая разбивает все вершины дерева на пути с одним важным свойством: поднимаясь от любой вершины до корня дерева придется сменить не более log(n) различных путей.
Для каждой вершины будем хранить следующие значения: 1) расстояние до корня дерева 2) количество потомков 3) предок (начало пути, на котором лежит вершина) 4) номер вершины, в которую выходит ребро из предка, ведущее в нашу вершину.
Все эти значения можно посчитать при построении декомпозиции.
Перейдем к вычислению LCA: Будем рекурсивно подниматься в направлении корня. Пусть на данной итерации рассматриваем вершины U, V. Сначала сравним вершины, в которые идут ребра из предков этих вершин. Если они совпадают, то, очевидно, или U лежит на пути от V к корню, или V лежит на пути от U к корню. Значит одна из них и является искомой. Выберем ту, чье расстояние до корня минимально.
Иначе нужно приблизить одну из этих вершин к корню, выбрав вместо нее ее предка. Приближать будем на основании того, какая из вершин останется дальше от корня, после приближения.
Очевидно, что в результате придем или в одну и ту же вершину, или одна из вершин окажется на пути ток корня к другой. Тем самым мы найдем LCA.
Ассимптотика
Очевидно, что для реализации алгоритма требуется O(n) памяти. Heavy-light декомпозиция строится за O(n). По свойству heavy-light декомпозиции, на пути от вершины к корню мы сменим не более log(n) путей. Значит время выполнения запроса также O(log(n)).