Изменения

Перейти к: навигация, поиск

Link-Cut Tree

507 байт добавлено, 22:50, 5 сентября 2019
м
Исправлена описка
'''Динамические деревья ''' (англ.''dynamic tree'') используются в двух областях: [[Определение сети, потока|потоки]] и динамические графы.
В первом случае динамические деревья позволяют построить эффективные алгоритмы для задачи о поиске максимального потока.
В динамическом графе периодически происходят изменения: появляются и исчезают ребра, меняются их веса. Изменения нужно Нужно быстро обрабатыватьизменения, а также уметь [[Отношение связности, компоненты связности | проверять связность]], [[Алгоритмы на деревьях|искать диаметр]]. Динамические деревья являются инструментом, который позволяет легко решать эти задачи. Динамические деревья особенно эффективны, когда нужно работать с большими деревьями и большим количеством запросов '''link''' и '''cut'''.
'''Link-cut tree''' {{---}} это структура данных, которая хранит [[Дерево, эквивалентные определения |лес деревьев]] и позволяет выполнять следующие операции:
* '''<tex>\mathrm{min(v)}</tex>''' {{---}} искать минимум на пути от вершины до корня;,* '''<tex>\mathrm{add(v, c)}</tex>''' {{---}} прибавлять константу на пути от вершины до корня;,* '''<tex>\mathrm{link(u, w)}</tex>''' {{---}} подвешивать одно дерево на другое;,
* '''<tex>\mathrm{cut(v)}</tex>''' {{---}} отрезать дерево с корнем в вершине <tex>\mathrm{v}</tex>.
Среднее время выполнения каждой операции {{---}} <tex>O(\log n)</tex>. Эта структура данных была придумана Робертом Тарьяном и Даниелем Слейтером в 1982 году.
[[Файл:Path_to_tree.png|250px|thumb|right|Пример построения дерева для пути]]
* '''<tex>\mathrm{add(v, c)}</tex>''' и '''<tex>\mathrm{min(v)}</tex>''' {{---}} прибавлять константу и искать минимум на некотором суффиксе (то есть на пути от вершины до корня), * '''<tex>\mathrm{cut(v)}</tex>''' {{---}} разбить один путь на два,* '''<tex>\mathrm{link(v, u)}</tex>''' {{---}} подвешивать голову одного пути к хвосту другого.
Если бы не последние две операции, то можно было бы применить [[Несогласованные поддеревья. Реализация массового обновления|дерево отрезков]], сложив в него вершины в том порядке в котором они идут в пути. Но непонятно, как сливать или разрезать деревья отрезков. Если Эту задачу можно решить при помощи [[Декартово дерево по неявному ключу|декартового дерева по неявному ключу]]. Также, если использовать какие-нибудь сливаемые деревья, то <tex>\mathrm{link}</tex> и <tex> \mathrm{cut}</tex> реализуются просто. Осталось научиться искать минимум и прибавлять константу на пути. Для этого, как и в деревьях отрезков, будем хранить дополнительные значения в вершинах.
В качестве сливаемых деревьев выберем [[Splay-дерево|splay-деревья]], в которых ключи выбираются равными глубине вершины.
==Link-cut tree==
Чтобы обобщить, разобьем дерево на множество непересекающихся путей. Каждое ребро обозначим либо solid-ребромсплошным, либо dashed-ребромпунктирным. Все пути в link-cut дереве хранятся в виде splay-деревьев. Корень каждого splay-дерева хранит указатель на вершину-родителя. В дальнейшем будем называть этот указатель <tex>pathparent</tex>.
[[Файл:Linkcut_paths.png|500px||center|Разбиение дерева на пути]]
===expose(u)===
Ключевая операция в link-cut-деревьях {{---}} <tex>\mathrm{expose(u)}</tex>. После её выполнения <tex>u</tex> лежит на одном пути с корнем link-cut дерева и при этом становится корнем в splay-дереве получившегося пути. Для этого она поднимается вверх по link-cut дереву, и если какой-нибудь путь пересекает путь от <tex>u</tex> до корня, то она его отрезает, разъединяя splay-дерево и делая соответствующее solid-сплошное ребро dashed-пунктирным ребром.
[[Файл:Linkcut_expose.png|500px||center|Разбиение дерева на пути]]
'''function''' expose(u: '''tree'''):
splay(u)
v <tex>\leftarrow=</tex> u
'''while''' v <tex> \ne </tex> root
p <tex>\leftarrow=</tex> pathparent(v) <font color=green>//получаем указатель на ближайшую вершину пути, пересекающего путь от u до корня</font>
splay(p) <font color=green>//теперь в правом поддереве p находятся вершины пути, которые находятся ниже чем p в link-cut-дереве,</font>
parent(right(p)) <tex>\leftarrow=</tex> null <font color=green>//поэтому правое поддерево p делаем новым путем</font> pathparent(right(p)) <tex>\leftarrow=</tex> p right(p) <tex>\leftarrow=</tex> v <font color=green>//объединяем оставшийся и построенный пути</font>
<tex>\vartriangle</tex>w(v) -= <tex>\vartriangle</tex>w(p)
<tex>\vartriangle</tex>min(p) <tex>\leftarrow=</tex> min{0, <tex>\vartriangle</tex>min(left(p)) + <tex>\vartriangle</tex>w(left(p)), <tex>\vartriangle</tex>min(right(p)) + <tex>\vartriangle</tex>w(right(p))} pathparent(v) <tex>\leftarrow=</tex> null v <tex>\leftarrow=</tex> p
splay(u)
===min(v)===
Построим splay-дерево для пути и сравним минимум вес корня <tex>v</tex> c минимумом в левом поддереве: '''intfunction''' min(v : '''tree'''):'''int'''
expose(v)
'''if''' <tex>\vartriangle</tex>min(left(v)) + <tex>\vartriangle</tex>w(left(v)) < <tex>\vartriangle</tex>w(v)
Если <tex>v</tex> {{---}} корень, а <tex>u</tex> {{---}} вершина в другом дереве, то <tex>\mathrm{link(v, u)}</tex> соединяет два дерева добавлением ребра <tex>(v, u)</tex>, причем <tex>u</tex> становится родителем <tex>v</tex>.
'''treefunction''' link(v: '''tree''', u: '''tree'''):'''tree'''
expose(v) <font color=green> //теперь v {{---}} корень в splay-дереве пути и не имеет левого ребенка(так как ключ равен глубине в link-cut дереве)</font>
expose(u)
<tex>\vartriangle</tex>w(u) -= <tex>\vartriangle</tex>w(v) <font color=green>//чтобы сделать u родителем v в link-cut дереве 1. делаем путь, содержащий u, левым ребенком v в splay-дереве</font>
parent(u) <tex>\leftarrow=</tex> v <font color=green>// 2. обновляем <tex>\vartriangle</tex>w, <tex>\vartriangle</tex>min</font> left(v) <tex>\leftarrow=</tex> u <tex>\vartriangle</tex>min(v) <tex>\leftarrow=</tex> min{0, <tex>\vartriangle</tex>min(u) + <tex>\vartriangle</tex>w(u), <tex>\vartriangle</tex>min(right(v)) + <tex>\vartriangle</tex>w((right(v)))}
'''return''' v
===cut(v)===
Отрезает дерево с корнем <tex>v</tex>. После вызова <tex>\mathrm{expose(v)}</tex> вершина <tex>v</tex> станет корнем splay-дерева, и в правом поддереве будут содержатся содержаться все вершины, которые были ниже <tex>v</tex> в link-cut дереве, а в левом {{---}} те что выше. Обнулив указатель на левого ребенка <tex>v</tex> и на родителя в левом поддереве, получим требуемое.
'''function''' cut(v: '''tree'''):
expose(v)
<tex>\vartriangle</tex>w(left(v)) += <tex>\vartriangle</tex>w(v)
<tex>\vartriangle</tex>min(v) <tex>\leftarrow=</tex> min{0, <tex>\vartriangle</tex>min(right(v)) + <tex>\vartriangle</tex>w(right(v))} parent(left(v)) <tex>\leftarrow=</tex> null parent(left(v)) <tex>\leftarrow=</tex> null
==Оценка времени работы==
}}
Операция <tex>u</tex> осуществляется с помощью последовательности преобразований dashed-пунктирного ребра в solid-сплошное ребро и другого solid-сплошного ребра в dashed-пунктирное ребро. Обозначим количество таких преобразований за <tex>M</tex>. Найдем количество преобразований сделанных в течение <tex>\mathrm{expose(u)}</tex>. Пусть <tex>H</tex> {{---}} множество всех тяжелых ребер, <tex>L</tex> {{---}} все легкие ребра, <tex>S \rightarrow D</tex> {{---}} множество solid-сплошных ребер, преобразованных в dashed пунктирные в течение одного <tex>\mathrm{expose}</tex>, <tex>D \rightarrow S</tex> {{---}} множество dashed-пунктирных ребер, преобразованных в solidсплошные.
<tex>M = |\{D \rightarrow S\}| = |\{L \cap D \rightarrow S\}| + |\{H \cap D \rightarrow S\}|</tex>
По лемме, количество легких dashed-пунктирных ребер, преобразованных в solidсплошные, будет не больше, чем <tex>\log n</tex>.
Обозначим за <tex>F</tex> лес деревьев, в которых каждое ребро либо solidсплошное, либо dashedпунктирное, a <tex>F'</tex> {{---}} лес, получившийся из <tex>F</tex> после одного вызова <tex>\mathrm{expose}</tex>. Определим потенциал <tex>\Phi _{a}(F) = n - 1 - |\{H \cap solid-edges\}|</tex>, <tex>\Delta \Phi _{a}</tex> {{---}} увеличение <tex>\Phi _{a}</tex> после одной операции <tex>\mathrm{expose}</tex>.
{{Лемма
Докажем, что [[Амортизационный анализ|амортизационная стоимость операции]] <tex>\mathrm{expose}</tex> равна <tex>O(\log n)</tex>.
Пусть <tex>s(v)</tex> {{---}} количество вершин в поддеревьях <tex>v</tex> ( здесь имеется в виду splay-дерево пути, котоый который строится в ходе выполнения <tex>\mathrm{expose}</tex>), <tex>r(v) = \log s(v)</tex>. По [[Splay-дерево#Lemma1|лемме]] стоимость ''i''-той операции <tex>\mathrm{splay}</tex> не превосходит <tex>1 + 3 \cdot (r(t) - r(v))</tex>. Это приводит к тому, что амортизационная стоимость <tex>\mathrm{expose}</tex> ограничена следующим значением:
<tex>3 \cdot \log n - 3 \cdot \log (s(v)) + M</tex>
===LCA===
C помощью link-cut-дерева можно найти наименьшего общего предка:
'''treefunction''' lca(u: '''tree''', v: '''tree'''):'''tree'''
expose(u)
expose(v)
==См. также==
*[[Метод двоичного подъема]]*[[Алгоритм Тарьяна поиска LCA за O(1) в оффлайн]]*[[Алгоритм Шибера-Вишкина]]
==Источники информации==
*[http://www.lektorium.tv/lecture/14464 А.С. Станкевич, "Дополнительные главы алгоритмов"]*[https://sites.google.com/site/indy256/algo/link-cut-tree-lca Реализация LCA на link-cut дереве]*[http://www.cs.cmu.edu/~sleator/papers/dynamic-trees.pdf D. Sleator and R. Tarjan, "A Data Structure for Dynamic Trees"]*[http://compgeom.cs.uiuc.edu/~jeffe/teaching/datastructures/2006/notes/07-linkcut.pdf Jeff Erickson. Lecture 7. Link-Cut Trees]*[http://planarity.org/Klein_splay_trees_and_link-cut_trees.pdf Optimization Algorithms for Planar Graphs. Splay trees and link-cut trees]*[http://en.wikipedia.org/wiki/Link/cut_tree Wikipedia {{---}} Link/cut tree]
[[Категория: Дискретная математика Алгоритмы и алгоритмыструктуры данных]]
[[Категория: Задача о наименьшем общем предке]]
24
правки

Навигация