Для большого класса задач требуется решить следующую вспомогательную задачу.
Задача. Дано корневое дерево. Требуется отвечать на запросы нахождения наименьшего общего предка вершин и , то есть вершины , которая лежит на пути от корня до , на пути от корня до , и при этом самую глубокую (нижнюю) из всех таких.
По-английский эта задача называется least common ancestor — наименьший общий предок.

Для лучшего понимания — медленно (за линейное время) наименьшего общего предка можно искать так:
bool ancestor(int u, int v) {
return tin[u] <= tin[v] && tin[v] < tout[u];
}
int lca(int u, int v) {
while (!ancestor(u, v))
u = p[u];
return u;
}
Есть много самых разных способов её решать, и далее в этой главе мы рассмотрим основные. Конкретно в этой статье мы сведем её к задаче нахождения минимума на отрезке (и наоборот).
#Сведение к Static RMQ
Пройдёмся по дереву dfs
-ом и выпишем два массива: глубины вершин и номера вершин. Записывать мы их будем как при входе в вершину, так и при выходе.

Пусть теперь поступил запрос: найти LCA вершин и . Для определенности предположим, что в обходе встретилась раньше: . Посмотрим на часть выписанного пути между моментом, когда мы вышли из и моментом, когда мы первый раз вошли в . Так как любой простой путь между двумя вершинами в дереве единственный, где-то на этом отрезке мы должны были прийти в наименьший общий предок. При этом мы на этом пути точно не поднимались выше LCA, а значит LCA — это самая высокая вершина на этом пути.
Получается, что чтобы найти LCA, можно найти позицию минимума на отрезке в массиве глубин (первый выписанный массив) и посмотреть, какой вершине она соответствует в эйлеровом обходе (второй выписанный массив). Таким образом, задачу LCA можно свести к задаче RMQ (нахождению минимума на отрезке), что можно сделать, например, деревом отрезков за на запрос.
Асимптотику времени запроса можно улучшить, используя тот факт, что мы на самом деле решаем задачу static RMQ, то есть у нас нет изменений массива. Для этого есть более подходящая структура — разреженная таблица, которая позволяет отвечать на запрос минимума за , но использует операций и памяти на препроцессинг с малой константой.
Оказывается, можно свести и обратно.
#Алгоритм Фарака-Колтона и Бендера
Disclaimer: алгоритм нахождения LCA, описываемый в этой секции, абсолютно бесполезен на практике, однако очень интересен с теоретической точки зрения.
Оказывается, что и LCA, и static RMQ можно считать за времени на запрос и времени на предподсчет.
На самом деле, в сведении LCA к RMQ, мы решаем не совсем полноценную задачу RMQ. Мы работаем не со всеми массивами целых чисел от 1 до , а только с некоторыми — с теми, в которых любые два элемента отличаются ровно на единицу, потому что каждый переход это либо спуск, либо подъём в dfs. Это ограничение позволяет находить минимум на подотрезках подобных массивов быстрее.
#Предподсчет
Сделаем следующее: раз каждый элемента либо на единицу больше, либо на единицу меньше предыдущего, то сопоставим исходному массиву глубин булевый массив размера : на -той позиции будет стоять единица, если следующее значение больше, и ноль если меньше. Этот массив нужно будет хранить в бинарном виде, чтобы можно было за константу получать булеву маску небольших подотрезков.
Первая часть предподсчёта. Возьмем константу , и разделим исходный массив на блоки по элементов. На каждом блоке посчитаем минимум, а над этими минимумами построим sparse table.
Всего блоков таких блоков , и поэтому построение работает за линейное время:
Вторая часть предподсчёта. Посчитаем для каждой возможной маски подъёмов / спусков размера максимальный спуск на ней — то есть пройдёмся по ней, поддерживая разницу встретившихся нулей и единиц, и запомним минимальное значение этого баланса. Это можно сделать за длину маски, помноженное на их количество:Возможных масок получается немного — ради этого мы и делили логарифм на два при определении
#Ответ на запрос
Теперь нам нужно с помощью посчитанных структур найти RMQ на каком-то отрезке . Он включает в себя какие-то последовательные блоки и сколько-то оставшихся ячеек слева и справа, не вошедших ни в какой целый блок.
Для блочной части мы можем просто сделать запрос в sparse table — он будет работать за константу.
Для обеих не-блочных частей посчитаем ещё по кандидату на ответ. Для этого нужно прибавить к граничному элементу предподсчитанное значение минимума на маске оставшихся неблочных элементов — её можно за константу получить битовыми операциями над булевым массивом.
Просто посчитать все суффиксные и префиксные минимумы для всех блоков, чтобы обрабатывать второй случай, к сожалению, нельзя — есть один частный случай, когда запрос маленький и не накрывает никакой блок целиком. В данном случае нужно просто взять граничный элемент и прибавить к нему минимум от нужной маски из массива подъёмов.
#Static RMQ → LCA
Этот алгоритм очень важен с теоретической точки зрения, потому что позволяет решать не только LCA, но и в общем случае static RMQ за линейный предподсчет и константу на запрос.
Построим декартово дерево, в котором в качестве ключей возьмём индексы элементов, а в качестве приоритетов возьмём сами значения. Декартово дерево могло получиться несбалансированным (так как нет рандомизации приоритетов), но это нам и не нужно. Дальше просто применим описанный выше алгоритм к этому дереву, и теперь для нахождения минимума в исходном массиве можно просто запросить общего предка -той и -той вершины в дереве — его приоритет в декартовом дереве и будет искомым минимумом.
Чуть более подробно и с реализацией (автор это никогда не кодил и вам не советует) можно почитать у Емакса. Впрочем, на практике этот алгоритм использовать нецелесообразно из-за большой константы: слишком много чего нужно считать, чтобы избавиться от логарифма в асимптотике.