<?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=178.95.251.99&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=178.95.251.99&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/178.95.251.99"/>
		<updated>2026-04-23T00:14:47Z</updated>
		<subtitle>Вклад участника</subtitle>
		<generator>MediaWiki 1.30.0</generator>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D0%B4%D0%B2%D0%BE%D0%B8%D1%87%D0%BD%D0%BE%D0%B3%D0%BE_%D0%BF%D0%BE%D0%B4%D1%8A%D1%91%D0%BC%D0%B0&amp;diff=20075</id>
		<title>Метод двоичного подъёма</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D0%B4%D0%B2%D0%BE%D0%B8%D1%87%D0%BD%D0%BE%D0%B3%D0%BE_%D0%BF%D0%BE%D0%B4%D1%8A%D1%91%D0%BC%D0%B0&amp;diff=20075"/>
				<updated>2012-03-27T18:29:22Z</updated>
		
		<summary type="html">&lt;p&gt;178.95.251.99: /* Псевдокод */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Метод двоичного подъема''' {{---}} один из самых простых методов для решения задачи [[Сведение задачи LCA к задаче RMQ|LCA]] в on-line. Он не использует метод решение задачи '''RMQ''' и основан на методе динамического программирования.&lt;br /&gt;
==Описание алгоритма==&lt;br /&gt;
Как и большинство '''on-line''' алгоритмов для решения задачи [[Сведение задачи LCA к задаче RMQ|LCA]], этот метод делает сначала препроцессинг, чтобы потом отвечать на запросы.&lt;br /&gt;
===Препроцессинг===&lt;br /&gt;
Препроцессинг заключается в том, чтобы посчитать функцию: &amp;lt;tex&amp;gt; dp[v][i] &amp;lt;/tex&amp;gt; {{---}} номер вершины, в которую мы придем если пройдем из вершины &amp;lt;tex&amp;gt; v &amp;lt;/tex&amp;gt; вверх по подвешенному дереву &amp;lt;tex&amp;gt; 2 ^ i &amp;lt;/tex&amp;gt; шагов, причем если мы пришли в корень, то мы там и останемся.&lt;br /&gt;
Для этого сначала обойдем дерево в глубину и для каждой вершины запишем номер ее родителя &amp;lt;tex&amp;gt; p[v] &amp;lt;/tex&amp;gt; и глубину вершины в подвешенном дереве &amp;lt;tex&amp;gt; d[v] &amp;lt;/tex&amp;gt;. Если &amp;lt;tex&amp;gt; v &amp;lt;/tex&amp;gt; {{---}} корень, то &amp;lt;tex&amp;gt; p[v] = v &amp;lt;/tex&amp;gt;. Тогда для функции &amp;lt;tex&amp;gt; dp &amp;lt;/tex&amp;gt; есть рекуррентная формула:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;dp[v][i]= \begin{cases}&lt;br /&gt;
p[v] &amp;amp; i = 0,\\&lt;br /&gt;
dp[dp[v][i - 1]][i - 1] &amp;amp; i &amp;gt; 0.&lt;br /&gt;
\end{cases}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Для того чтобы отвечать на запросы нам нужны будут только те значения &amp;lt;tex&amp;gt; dp[v][i] &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; i \le \log_2{n} &amp;lt;/tex&amp;gt;, ведь при больших &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; значение &amp;lt;tex&amp;gt; dp[v][i] &amp;lt;/tex&amp;gt; будет номером корня. &lt;br /&gt;
&lt;br /&gt;
Всего состояний динамики &amp;lt;tex&amp;gt; O(n \log{n})&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt; {{---}} это количество вершин в дереве. Каждое состояние считается за &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt;. Поэтому суммарная сложность времени и памяти препроцессинга {{---}} &amp;lt;tex&amp;gt; O(n \log{n}) &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Ответы на запросы===&lt;br /&gt;
Ответы на запросы будут происходить за время &amp;lt;tex&amp;gt; O(\log{n})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
Для ответа на запрос заметим сначала, что если &amp;lt;tex&amp;gt; c = LCA(v, u) &amp;lt;/tex&amp;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; d[c] \le min(d[v], d[u])&amp;lt;/tex&amp;gt;. Поэтому если &amp;lt;tex&amp;gt; d[v] &amp;lt; d[u] &amp;lt;/tex&amp;gt;, то пройдем от вершины &amp;lt;tex&amp;gt; u &amp;lt;/tex&amp;gt; на &amp;lt;tex&amp;gt; (d[u] - d[v]) &amp;lt;/tex&amp;gt; шагов вверх, это и будет новое значение &amp;lt;tex&amp;gt; u &amp;lt;/tex&amp;gt; и это можно сделать за &amp;lt;tex&amp;gt; O(\log{n}) &amp;lt;/tex&amp;gt;. Можно записать число &amp;lt;tex&amp;gt; (d[u] - d[v]) &amp;lt;/tex&amp;gt; в двоичной системе, это представление этого число в виде суммы степеней двоек, &amp;lt;tex&amp;gt; 2 ^ {i_1} + 2 ^ {i_2} + \ldots + 2 ^ {i_l} &amp;lt;/tex&amp;gt; и для всех &amp;lt;tex&amp;gt; j&amp;lt;/tex&amp;gt; пройти вверх последовательно из вершины &amp;lt;tex&amp;gt; u &amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt; dp[u][i_j] &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Дальше считаем, что &amp;lt;tex&amp;gt; d[v] = d[u] &amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;tex&amp;gt; v = u &amp;lt;/tex&amp;gt;, то ответ на запрос &amp;lt;tex&amp;gt; v &amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
А если &amp;lt;tex&amp;gt; v \neq u &amp;lt;/tex&amp;gt;, то найдем такие вершины &amp;lt;tex&amp;gt; x &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; y &amp;lt;/tex&amp;gt;, такие что &amp;lt;tex&amp;gt; x \neq y &amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt; x &amp;lt;/tex&amp;gt; {{---}} предок &amp;lt;tex&amp;gt; v &amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt; y &amp;lt;/tex&amp;gt; {{---}} предок &amp;lt;tex&amp;gt; u &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; p[x] = p[y] &amp;lt;/tex&amp;gt;. Тогда ответом на запрос будет &amp;lt;tex&amp;gt; p[x] &amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Научимся находить эти вершины &amp;lt;tex&amp;gt; x &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; y &amp;lt;/tex&amp;gt;. Для этого сначала инициализируем &amp;lt;tex&amp;gt; x = v &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; y = u &amp;lt;/tex&amp;gt;. Дальше на каждом шаге находим такое максимальное &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt; dp[x][k] \neq dp[y][k] &amp;lt;/tex&amp;gt;. И проходим из вершин &amp;lt;tex&amp;gt; x &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; y &amp;lt;/tex&amp;gt; на &amp;lt;tex&amp;gt; 2 ^ k &amp;lt;/tex&amp;gt; шагов вверх. Если такого &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; найти нельзя, то значения &amp;lt;tex&amp;gt; x &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; y &amp;lt;/tex&amp;gt;, это те самые вершины, которые нам требуется найти, ведь &amp;lt;tex&amp;gt; p[x] = dp[x][0] = dp[y][0] = p[y] &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Оценим время работы. Заметим, что найденные &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; строго убывают. Во-первых, потому что мы находим на каждом шаге максимальное значение &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt;, а во-вторых, два раза подряд мы одно и то же &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; получить не можем, так как тогда получилось бы, что можно пройти &amp;lt;tex&amp;gt; 2 ^ k + 2 ^ k = 2 ^ {k + 1}&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; O(\log{n}) &amp;lt;/tex&amp;gt; значений &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt;, их можно перебирать в порядке убывания. Сложность ответа на запрос &amp;lt;tex&amp;gt; O(\log{n}) &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Псевдокод==&lt;br /&gt;
&amp;lt;big&amp;gt;&lt;br /&gt;
  preprocess()&lt;br /&gt;
    p := dfs(0)&lt;br /&gt;
    for i := 1 .. n&lt;br /&gt;
      dp[i][0] := p[i]&lt;br /&gt;
    for j := 1 .. log(n)&lt;br /&gt;
      for i := 1 .. n&lt;br /&gt;
        dp[i][j] := dp[dp[i][j - 1]][j - 1]&lt;br /&gt;
&lt;br /&gt;
  lca(v, u)&lt;br /&gt;
    if (d[v] &amp;gt; d[u])&lt;br /&gt;
      swap(v, u)&lt;br /&gt;
    for i := log(n) .. 0&lt;br /&gt;
      if (d[u] - d[v] &amp;gt;= &amp;lt;tex&amp;gt; 2 ^ i &amp;lt;/tex&amp;gt;)&lt;br /&gt;
        u := dp[u][i]&lt;br /&gt;
    if (v = u)&lt;br /&gt;
      return v&lt;br /&gt;
    for i := log(n) .. 0&lt;br /&gt;
      if (dp[v][i] &amp;lt;&amp;gt; dp[u][i])&lt;br /&gt;
        v := dp[v][i]&lt;br /&gt;
        u := dp[u][i]&lt;br /&gt;
    return p[v]&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
* [[Сведение задачи LCA к задаче RMQ]]&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/LCA LCA on Wikipedia]&lt;br /&gt;
* [http://www.topcoder.com/tc?module=Static&amp;amp;d1=tutorials&amp;amp;d2=lowestCommonAncestor TopCoder tutorial: RMQ and LCA]&lt;br /&gt;
* [http://e-maxx.ru/algo/lca_simpler Метод двоичного подъема - e-maxx.ru]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория: Задача о наименьшем общем предке]]&lt;/div&gt;</summary>
		<author><name>178.95.251.99</name></author>	</entry>

	</feed>