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