Heavy-light декомпозиция — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
Строка 1: Строка 1:
'''Heavy-light декомпозиция''' {{---}} техника разбиения дерева на множество рёберно-непересекающихся путей так, чтобы при прохождении от одной вершины до другой произошла смена не более, чем <tex>O(\log{n})</tex> путей.
+
'''Heavy-light декомпозиция''' {{---}} техника разбиения дерева на множество путей для решения задач о запросах на пути в дереве (в том числе с модификациями).
 
==Описание задачи==
 
==Описание задачи==
Пусть у нас есть дерево <tex>T</tex> c <tex>n</tex> вершинами и нам нужно проводить операции на нем на пути от вершины <tex>v</tex> до вершины <tex>u</tex>. Множество подобных запросов делаются за время <tex>O(\log^p{n})</tex> с помощью Heavy-light декомпозиции.
+
{{Задача
 +
|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) \geq</tex> <tex dpi="150">\frac{s(u)}{2}</tex>.
+
'''Тяжёлые ребра''' {{---}} ребра <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>Таким образом все рёбра будут покрыты.
1. Все рёбра покрыты путями.
+
# Все пути не пересекаются. <br>Докажем от противного. Пусть мы взяли какое-то ребро дважды. Это значит, что из какой-то вершины вело более чем одно тяжёлое ребро, чего быть не могло. Получили противоречие.
 
+
# При прохождении пути от вершины <tex>v</tex> до вершины <tex>u</tex> произойдет смена не более, чем <tex>O(\log{n})</tex> путей. <br>Докажем эквивалентный факт, что при пути от любой вершины до корня мы сменим не более, чем <tex>O(\log{n})</tex> путей. Рассмотрим лёгкое ребро. Заметим, что проход вниз по такому ребру уменьшает размер поддерева как минимум в 2 раза. Но смена пути может произойти только при переходе по лёгкому ребру. Таким образом мы сменим не более <tex>O(\log{n})</tex> путей.
Есть два типа вершин: те, из которых ведёт ровно одно тяжёлое ребро и те, из которых не ведёт ни одного тяжёлого ребра. Для первого типа вершин мы дойдем до них некоторым путём через тяжёлое ребро снизу по определению выбора путей, а лёгкие рёбра ведущие из неё возьмем как последние рёбра в соответствующих путях. Для второго типа вершин мы по определению выбора путей возьмем их как начальные и пойдем вверх.
 
 
 
Таким образом все рёбра будут покрыты.
 
 
 
2. Все пути не пересекаются.
 
 
 
Докажем от противного. Пусть мы взяли какое-то ребро дважды. Это значит, что из какой-то вершины вело более чем одно тяжёлое ребро, чего быть не могло. Получили противоречие.
 
 
 
3. При прохождении пути от вершины <tex>v</tex> до вершины <tex>u</tex> произойдет смена не более, чем <tex>O(\log{n})</tex> путей.
 
 
 
Докажем эквивалентный факт, что при пути от любой вершины до корня мы сменим не более, чем <tex>O(\log{n})</tex> путей. Рассмотрим лёгкое ребро. Заметим, что проход вниз по такому ребру уменьшает размер поддерева как минимум в 2 раза. Но смена пути может произойти только при переходе по лёгкому ребру. Таким образом мы сменим не более <tex>O(\log{n})</tex> путей.
 
 
}}
 
}}
  
Строка 75: Строка 68:
 
     '''return''' res
 
     '''return''' res
 
</code>
 
</code>
 
==Примечание==
 
Существует вариант Heavy-light декомпозиции на вершинно-непересекающихся путях. Чтобы получить такой путь нужно всего-лишь выкинуть последнее ребро из всех путей в рёберно-непересекающейся декомпозиции.
 
 
 
== См.также ==
 
== См.также ==
 
*[[Метод двоичного подъема]]
 
*[[Метод двоичного подъема]]
 
*[[Дерево отрезков. Построение]]
 
*[[Дерево отрезков. Построение]]
 +
*[[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 декомпозиция — техника разбиения дерева на множество путей для решения задач о запросах на пути в дереве (в том числе с модификациями).

Описание задачи

Задача:
Пусть у нас есть дерево [math]T[/math] c [math]n[/math] вершинами и нам нужно проводить операции на нем на пути от вершины [math]v[/math] до вершины [math]u[/math]. (Например сумма на пути с модификацией прибавления на пути)

Множество подобных запросов делаются за время [math]O(\log^2{n})[/math] с помощью Heavy-light декомпозиции.

Описание декомпозиции

Пример разбиения на лёгкие/тяжёлые рёбра.

Декомпозиция заключается в классификации всех рёбер дерева [math]T[/math] в 2 вида: легкие и тяжёлые. Введём функцию [math]s(v)[/math], которая будет обозначать размер поддерева вершины [math]v[/math].

Тяжёлые ребра — ребра [math](u, v)[/math] такие, что [math]s(v) \geqslant[/math] [math]\frac{s(u)}{2}[/math].

Лёгкие ребра — соответственно все остальные.

Очевидно, что из вершины может выходить как максимум одно тяжёлое ребро, т.к. иначе у нас есть два поддерева по как минимум [math]\frac{s(u)}{2}[/math] вершин, а также сама вершина [math]u[/math]. Итого [math]s(u) + 1[/math] вершин, тогда как у нас всего [math]s(u)[/math] вершин в поддереве.

Теперь рассмотрим вершины, из которых не ведет ни одно тяжёлое ребро. Будем идти от них вверх до корня или пока не пройдем легкое ребро. Получится какое-то множество путей. Утверждается, что оно будет являться искомым.

Существует вариант Heavy-light декомпозиции на вершинно-непересекающихся путях. Чтобы получить такой путь нужно всего-лишь выкинуть последнее ребро из всех путей в рёберно-непересекающейся декомпозиции.

Доказательство корректности полученной декомпозиции

Утверждение:
Полученная декомпозиция является искомой.
[math]\triangleright[/math]

Докажем по отдельности корректность декомпозиции.

  1. Все рёбра покрыты путями.
    Есть два типа вершин: те, из которых ведёт ровно одно тяжёлое ребро и те, из которых не ведёт ни одного тяжёлого ребра. Для первого типа вершин мы дойдем до них некоторым путём через тяжёлое ребро снизу по определению выбора путей, а лёгкие рёбра ведущие из неё возьмем как последние рёбра в соответствующих путях. Для второго типа вершин мы по определению выбора путей возьмем их как начальные и пойдем вверх.
    Таким образом все рёбра будут покрыты.
  2. Все пути не пересекаются.
    Докажем от противного. Пусть мы взяли какое-то ребро дважды. Это значит, что из какой-то вершины вело более чем одно тяжёлое ребро, чего быть не могло. Получили противоречие.
  3. При прохождении пути от вершины [math]v[/math] до вершины [math]u[/math] произойдет смена не более, чем [math]O(\log{n})[/math] путей.
    Докажем эквивалентный факт, что при пути от любой вершины до корня мы сменим не более, чем [math]O(\log{n})[/math] путей. Рассмотрим лёгкое ребро. Заметим, что проход вниз по такому ребру уменьшает размер поддерева как минимум в 2 раза. Но смена пути может произойти только при переходе по лёгкому ребру. Таким образом мы сменим не более [math]O(\log{n})[/math] путей.
[math]\triangleleft[/math]

Применение

Сумма на пути

Классическая задача о сумме на пути в дереве с [math]n[/math] решается с помощью Heavy-light декомпозиции за время. Возможны модификации веса.

Построим дерево отрезков над каждым путём. Рассмотрим запрос [math]sum(u, v)[/math]. Давайте найдем вершину [math]c[/math], которая является [math]LCA(u, v)[/math] (например с помощью двоичного подъема. Мы разбили запрос на два: [math](u, c)[/math] и [math](c, v)[/math], на каждый из которых можно легко ответить выбирая путь, содержащий самую нижнюю вершину и вырезать его, пока этот путь не содержит [math]c[/math].

Так мы сможем получать ответ на пути за [math]O(\log{n})[/math]. А всего таких путей нужно будет рассмотреть [math]O(\log{n})[/math]. Итого мы способны решить эту задачу за время [math]O(\log^2{n})[/math].

Хоть это и не самый эффективный способ для решения этой задачи, но можно заметить, что с помощью такого трюка мы можем решить широкий класс задач просто сведя её к задаче о запросах в дереве отрезков (такие как максимум на пути, покраска на пути, прибавление на пути и др.).

Реализация

Опущены некоторые детали реализации: построение и предподсчёт. [math]path[/math] — массив, хранящий в себе номер пути в которому относится конкретная вершина, [math]pathRoot[/math] — корень пути (самая верхняя вершина), [math]pathPos[/math] — массив, хранящий смещение вершины в пути относительно корня пути, [math]value[i][j][/math] — вес [math]j[/math]-ого ребра в [math]i[/math]-ом пути.

Пример реализации запроса суммы на пути:

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

См.также

Источники информации