Изменения

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

Метод двоичного подъёма

6558 байт добавлено, 19:39, 4 сентября 2022
м
rollbackEdits.php mass rollback
'''Метод двоичного подъёма''' {{В разработке---}}один из самых простых методов для решения задачи [[Сведение задачи LCA к задаче RMQ|LCA]] в online. Он не использует метод решения задачи '''RMQ''' и основан на методе [[Динамическое программирование | динамического программирования]].==Описание алгоритма==Как и большинство '''on-line''' алгоритмов для решения задачи [[Сведение задачи LCA к задаче RMQ|LCA]], этот метод делает сначала препроцессинг, чтобы потом отвечать на запросы.===Препроцессинг===Препроцессинг заключается в том, чтобы посчитать функцию: <tex> dp[v][i] </tex> {{---}} номер вершины, в которую мы придём если пройдём из вершины <tex> v </tex> вверх по подвешенному дереву <tex> 2 ^ i </tex> шагов, причём если мы пришли в корень, то мы там и останемся.Для этого сначала обойдем дерево в глубину, и для каждой вершины запишем номер её родителя <tex> p[v] </tex> и глубину вершины в подвешенном дереве <tex> d[v] </tex>. Если <tex> v </tex> {{---}} корень, то <tex> p[v] = v </tex>. Тогда для функции <tex> dp </tex> есть рекуррентная формула: <tex>dp[v][i]= \begin{cases}p[v] & i = 0,\\dp[dp[v][i - 1]][i - 1] & i \: > \: 0.\end{cases}</tex> Для того чтобы отвечать на запросы нам нужны будут только те значения <tex> dp[v][i] </tex>, где <tex> i \leqslant \log_2{n} </tex>, ведь при больших <tex> i </tex> значение <tex> dp[v][i] </tex> будет номером корня.
Всего состояний динамики <tex> O(n \log{n})</tex>, где <tex> n </tex> {{---}} это количество вершин в дереве. Каждое состояние считается за <tex> O(1) </tex>. Поэтому суммарная сложность времени и памяти препроцессинга {{---}} <tex> O(n \log{n}) </tex>. ===Ответы на запросы=Описание алгоритма==Метод двоичного подъема Ответы на запросы будут происходить за время <tex> O(\log{n})</tex>.Для ответа на запрос заметим сначала, что если <tex> c = LCA(v, u) </tex>, для некоторых <tex> v </tex> и <tex> u </tex>, то <tex> d[c] \leqslant \min(d[v], d[u])</tex>. Поэтому если <tex> d[v] < d[u] </tex>, то пройдём от вершины <tex> u </tex> на <tex> (d[u] - d[v]) </tex> шагов вверх, это и будет новое значение <tex> u </tex> и это можно сделать за <tex> O(\log{n}) </tex>. Можно записать число <tex> (d[u] - d[v]) </tex> в двоичной системе, это один представление этого число в виде суммы степеней двоек, <tex> 2 ^ {i_1} + 2 ^ {i_2} + \ldots + 2 ^ {i_l} </tex> и для всех <tex> i_j</tex> пройти вверх последовательно из самых простых методов для решения задачи LCA вершины <tex> u </tex> в on<tex> dp[u][i_j] </tex>. Дальше считаем, что <tex> d[v] = d[u] </tex>.  Если <tex> v = u </tex>, то ответ на запрос <tex> v </tex>.  А если <tex> v \neq u </tex>, то найдём такие вершины <tex> x </tex> и <tex> y </tex>, такие что <tex> x \neq y </tex>, <tex> x </tex> {{---}} предок <tex> v </tex>, <tex> y </tex> {{---line }} предок <tex> u </tex> и <tex> p[x] = p[y] </tex>. Тогда ответом на запрос будет <tex> p[x] </tex>.  Научимся находить эти вершины <tex> x </tex> и <tex> y </tex>. Для этого сначала инициализируем <tex> x = v </tex> и <tex> y = u </tex>. Дальше на каждом шаге находим такое максимальное <tex> k </tex>, что <tex> dp[x][k] \neq dp[y][k] </tex>. И проходим из вершин <tex> x </tex> и <tex> y </tex> на <tex> 2 ^ k </tex> шагов вверх. Если такого <tex> k </tex> найти нельзя, то значения <tex> x </tex> и он <tex> y </tex>, это те самые вершины, которые нам требуется найти, ведь <tex> p[x] = dp[x][0] = dp[y][0] = p[y] </tex>. Оценим время работы. Заметим, что найденные <tex> k </tex> строго убывают. Во-первых, потому что мы находим на каждом шаге максимальное значение <tex> k </tex>, а во-вторых, два раза подряд мы одно и то же <tex> k </tex> получить не использует метод решение можем, так как тогда получилось бы, что можно пройти <tex> 2 ^ k + 2 ^ k = 2 ^ {k + 1}</tex> шагов, а значит вместо первого <tex> k </tex>, мы бы нашли <tex> k + 1 </tex>. А, значит, всего <tex> O(\log{n}) </tex> значений <tex> k </tex>, их можно перебирать в порядке убывания. Сложность ответа на запрос <tex> O(\log{n}) </tex>. ==Псевдокод== '''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[dp[u][i]] - d[v] >= 0 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] ==См. также==* [[Сведение задачи LCA к задаче RMQ]] ==Источники информации==* [http://en. Он основан на методе динамического программированияwikipedia.org/wiki/Lowest_common_ancestor Wikipedia: LCA]* [http://www.topcoder.com/tc?module=Static&d1=tutorials&d2=lowestCommonAncestor TopCoder tutorial: RMQ and LCA]* [http://e-maxx.ru/algo/lca_simpler MAXimal :: algo :: Метод двоичного подъёма ][[Категория: Алгоритмы и структуры данных]][[Категория: Задача о наименьшем общем предке]]
1632
правки

Навигация