Heavy-light декомпозиция — различия между версиями
Строка 1: | Строка 1: | ||
− | '''Heavy-light декомпозиция''' {{---}} техника разбиения дерева на множество | + | '''Heavy-light декомпозиция''' {{---}} техника разбиения дерева на множество путей для решения задач о запросах на пути в дереве (в том числе с модификациями). |
==Описание задачи== | ==Описание задачи== | ||
− | Пусть у нас есть дерево <tex>T</tex> c <tex>n</tex> вершинами и нам нужно проводить операции на нем на пути от вершины <tex>v</tex> до вершины <tex>u</tex>. Множество подобных запросов делаются за время <tex>O(\log^ | + | {{Задача |
+ | |definition=Пусть у нас есть дерево <tex>T</tex> c <tex>n</tex> вершинами и нам нужно проводить операции на нем на пути от вершины <tex>v</tex> до вершины <tex>u</tex>. (Например сумма на пути с модификацией прибавления на пути)}} | ||
+ | Множество подобных запросов делаются за время <tex>O(\log^2{n})</tex> с помощью Heavy-light декомпозиции. | ||
==Описание декомпозиции== | ==Описание декомпозиции== | ||
[[Файл:Heavylight.png|thumb|right|300x150px|Пример разбиения на лёгкие/тяжёлые рёбра.]] | [[Файл:Heavylight.png|thumb|right|300x150px|Пример разбиения на лёгкие/тяжёлые рёбра.]] | ||
Декомпозиция заключается в классификации всех рёбер дерева <tex>T</tex> в 2 вида: легкие и тяжёлые. Введём функцию <tex>s(v)</tex>, которая будет обозначать размер поддерева вершины <tex>v</tex>. | Декомпозиция заключается в классификации всех рёбер дерева <tex>T</tex> в 2 вида: легкие и тяжёлые. Введём функцию <tex>s(v)</tex>, которая будет обозначать размер поддерева вершины <tex>v</tex>. | ||
− | '''Тяжёлые ребра''' {{---}} ребра <tex>(u, v)</tex> такие, что <tex>s(v) \ | + | '''Тяжёлые ребра''' {{---}} ребра <tex>(u, v)</tex> такие, что <tex>s(v) \geqslant</tex> <tex dpi="150">\frac{s(u)}{2}</tex>. |
'''Лёгкие ребра''' {{---}} соответственно все остальные. | '''Лёгкие ребра''' {{---}} соответственно все остальные. | ||
Строка 13: | Строка 15: | ||
Теперь рассмотрим вершины, из которых не ведет ни одно тяжёлое ребро. Будем идти от них вверх до корня или пока не пройдем легкое ребро. Получится какое-то множество путей. Утверждается, что оно будет являться искомым. | Теперь рассмотрим вершины, из которых не ведет ни одно тяжёлое ребро. Будем идти от них вверх до корня или пока не пройдем легкое ребро. Получится какое-то множество путей. Утверждается, что оно будет являться искомым. | ||
+ | |||
+ | Существует вариант Heavy-light декомпозиции на вершинно-непересекающихся путях. Чтобы получить такой путь нужно всего-лишь выкинуть последнее ребро из всех путей в рёберно-непересекающейся декомпозиции. | ||
==Доказательство корректности полученной декомпозиции== | ==Доказательство корректности полученной декомпозиции== | ||
{{Утверждение | {{Утверждение | ||
Строка 18: | Строка 22: | ||
|proof = | |proof = | ||
Докажем по отдельности корректность декомпозиции. | Докажем по отдельности корректность декомпозиции. | ||
− | + | # Все рёбра покрыты путями. <br>Есть два типа вершин: те, из которых ведёт ровно одно тяжёлое ребро и те, из которых не ведёт ни одного тяжёлого ребра. Для первого типа вершин мы дойдем до них некоторым путём через тяжёлое ребро снизу по определению выбора путей, а лёгкие рёбра ведущие из неё возьмем как последние рёбра в соответствующих путях. Для второго типа вершин мы по определению выбора путей возьмем их как начальные и пойдем вверх. <br>Таким образом все рёбра будут покрыты. | |
− | + | # Все пути не пересекаются. <br>Докажем от противного. Пусть мы взяли какое-то ребро дважды. Это значит, что из какой-то вершины вело более чем одно тяжёлое ребро, чего быть не могло. Получили противоречие. | |
− | + | # При прохождении пути от вершины <tex>v</tex> до вершины <tex>u</tex> произойдет смена не более, чем <tex>O(\log{n})</tex> путей. <br>Докажем эквивалентный факт, что при пути от любой вершины до корня мы сменим не более, чем <tex>O(\log{n})</tex> путей. Рассмотрим лёгкое ребро. Заметим, что проход вниз по такому ребру уменьшает размер поддерева как минимум в 2 раза. Но смена пути может произойти только при переходе по лёгкому ребру. Таким образом мы сменим не более <tex>O(\log{n})</tex> путей. | |
− | Есть два типа вершин: те, из которых ведёт ровно одно тяжёлое ребро и те, из которых не ведёт ни одного тяжёлого ребра. Для первого типа вершин мы дойдем до них некоторым путём через тяжёлое ребро снизу по определению выбора путей, а лёгкие рёбра ведущие из неё возьмем как последние рёбра в соответствующих путях. Для второго типа вершин мы по определению выбора путей возьмем их как начальные и пойдем вверх. | ||
− | |||
− | Таким образом все рёбра будут покрыты. | ||
− | |||
− | |||
− | |||
− | Докажем от противного. Пусть мы взяли какое-то ребро дважды. Это значит, что из какой-то вершины вело более чем одно тяжёлое ребро, чего быть не могло. Получили противоречие. | ||
− | |||
− | |||
− | |||
− | Докажем эквивалентный факт, что при пути от любой вершины до корня мы сменим не более, чем <tex>O(\log{n})</tex> путей. Рассмотрим лёгкое ребро. Заметим, что проход вниз по такому ребру уменьшает размер поддерева как минимум в 2 раза. Но смена пути может произойти только при переходе по лёгкому ребру. Таким образом мы сменим не более <tex>O(\log{n})</tex> путей. | ||
}} | }} | ||
Строка 75: | Строка 68: | ||
'''return''' res | '''return''' res | ||
</code> | </code> | ||
− | |||
− | |||
− | |||
− | |||
== См.также == | == См.также == | ||
*[[Метод двоичного подъема]] | *[[Метод двоичного подъема]] | ||
*[[Дерево отрезков. Построение]] | *[[Дерево отрезков. Построение]] | ||
+ | *[[Link-Cut Tree]] | ||
− | == | + | ==Источники информации== |
*[http://en.wikipedia.org/wiki/Heavy_path_decomposition Wikipedia — Heavy path decomposition] | *[http://en.wikipedia.org/wiki/Heavy_path_decomposition Wikipedia — Heavy path decomposition] | ||
*[http://e-maxx.ru/algo/heavy_light MAXimal :: algo :: Heavy-light декомпозиция] | *[http://e-maxx.ru/algo/heavy_light MAXimal :: algo :: Heavy-light декомпозиция] | ||
Строка 89: | Строка 79: | ||
[[Категория: Алгоритмы и структуры данных]] | [[Категория: Алгоритмы и структуры данных]] | ||
[[Категория: Задача о наименьшем общем предке]] | [[Категория: Задача о наименьшем общем предке]] | ||
+ | [[Категория: Структуры данных]] |
Версия 09:03, 8 июня 2015
Heavy-light декомпозиция — техника разбиения дерева на множество путей для решения задач о запросах на пути в дереве (в том числе с модификациями).
Содержание
Описание задачи
Задача: |
Пусть у нас есть дерево | c вершинами и нам нужно проводить операции на нем на пути от вершины до вершины . (Например сумма на пути с модификацией прибавления на пути)
Множество подобных запросов делаются за время
с помощью Heavy-light декомпозиции.Описание декомпозиции
Декомпозиция заключается в классификации всех рёбер дерева
в 2 вида: легкие и тяжёлые. Введём функцию , которая будет обозначать размер поддерева вершины .Тяжёлые ребра — ребра
такие, что .Лёгкие ребра — соответственно все остальные.
Очевидно, что из вершины может выходить как максимум одно тяжёлое ребро, т.к. иначе у нас есть два поддерева по как минимум
вершин, а также сама вершина . Итого вершин, тогда как у нас всего вершин в поддереве.Теперь рассмотрим вершины, из которых не ведет ни одно тяжёлое ребро. Будем идти от них вверх до корня или пока не пройдем легкое ребро. Получится какое-то множество путей. Утверждается, что оно будет являться искомым.
Существует вариант Heavy-light декомпозиции на вершинно-непересекающихся путях. Чтобы получить такой путь нужно всего-лишь выкинуть последнее ребро из всех путей в рёберно-непересекающейся декомпозиции.
Доказательство корректности полученной декомпозиции
Утверждение: |
Полученная декомпозиция является искомой. |
Докажем по отдельности корректность декомпозиции.
|
Применение
Сумма на пути
Классическая задача о сумме на пути в дереве с
решается с помощью Heavy-light декомпозиции за время. Возможны модификации веса.Построим дерево отрезков над каждым путём. Рассмотрим запрос . Давайте найдем вершину , которая является (например с помощью двоичного подъема. Мы разбили запрос на два: и , на каждый из которых можно легко ответить выбирая путь, содержащий самую нижнюю вершину и вырезать его, пока этот путь не содержит .
Так мы сможем получать ответ на пути за
. А всего таких путей нужно будет рассмотреть . Итого мы способны решить эту задачу за время .Хоть это и не самый эффективный способ для решения этой задачи, но можно заметить, что с помощью такого трюка мы можем решить широкий класс задач просто сведя её к задаче о запросах в дереве отрезков (такие как максимум на пути, покраска на пути, прибавление на пути и др.).
Реализация
Опущены некоторые детали реализации: построение и предподсчёт.
— массив, хранящий в себе номер пути в которому относится конкретная вершина, — корень пути (самая верхняя вершина), — массив, хранящий смещение вершины в пути относительно корня пути, — вес -ого ребра в -ом пути.Пример реализации запроса суммы на пути:
function queryPath(int path, int from, int to): int res = 0 while from <= to: if from % 2 == 1: res += value[path][from] if to % 2 == 0: res += value[path][to] to = (to - 1) / 2 from = (from + 1) / 2 return res
function query(int u, int v): int res = 0 int root = pathRoot[path[u]]; while !isAncestor(root, v) res += queryPath(path[u], 0, pathPos[u]); u = parent[root] root = pathRoot[path[u]] root = pathRoot[path[v]]; while !isAncestor(root, u) res += queryPath(path[v], 0, pathPos[v]); v = parent[root] root = pathRoot[path[v]] res += queryPath(path[u], min(pathPos[u], pathPos[v]), max(pathPos[u], pathPos[v])) return res