Skip to content
Snippets Groups Projects
Commit c57e1f1c authored by Ondřej Mička's avatar Ondřej Mička
Browse files

Graphs: Intro to link-cut; minor changes

parent 1185c0e3
No related branches found
No related tags found
No related merge requests found
......@@ -6,23 +6,25 @@
\def\TODO{{\bf TODO}}
\def\NOTE{{\bf NOTE}}
\def\LCA{\op{LCA}}
\def\Root{\op{Root}}
\def\Expose{\op{Expose}}
\def\Parent{\op{Parent}}
\chapter[graphs]{Representation of graphs}
In this chapter we will peek into the area of data structures for representation of
graphs. Our ultimate goal is to design a data structure that represents a forest with
weighted vertices and allows efficient path queries (e.g. what is the lightest edge on the
path between $u$ and $v$) along with weight and structural updates.
weighted vertices and allows efficient path queries (e.g. what is the cheapest vertex on the
path between $u$ and $v$) along with cost and structural updates.
Let us define the problem more formally. We wish to represent a forest $F = (V,
E)$, where each vertex~$v$ has weight~$w(v)\in\R$.\foot{We could also had weighted edges
E)$, where each vertex~$v$ has cost~$c(v)\in\R$.\foot{We could also had weighted edges
instead.} We would like to support following operations:
\tightlist{o}
\:\em{path query} --- find the vertex with minimum weight on a path $u\to
\:\em{path query} --- find the vertex with minimum cost on a path $u\to
v$;\foot{Generally, we can use any associative operation instead of minimum.}
\:\em{point update} --- set $w(v) \leftarrow c\in\R$;
\:\em{path update} --- increase weight of each vertex on path $u\to v$ by $\delta\in\R$;
\:\em{structural update} --- connect/disconnect two trees via edge $(u,v)$.
\:\em{point update} --- set $c(v) \leftarrow d\in\R$;
\:\em{path update} --- increase cost of each vertex on the path $u\to v$ by $\delta\in\R$;
\:\em{structural update} --- connect two trees via edge $(u,v)$ or delete edge $(u,v)$.
\endlist
......@@ -31,9 +33,9 @@ As a warm-up we build a data structure for $F$ being a static path, without stru
updates. This will also be an important building block for the more general case.
Let us denote the vertices $v_1, \dots, v_n$ according to the position on the path and let
us denote $w_i = w(v_i)$. \TODO\foot{maybe change initial notation} We build an range
tree~$T$ over the weights $w_1, \dots, w_n$. That is, $T$ is a complete binary tree with
$w_1,\dots w_n$ in its leaves (in this order) and inner nodes contain the minimum of their
us denote $c_i = c(v_i)$. We build an range
tree~$T$ over the costs $c_1, \dots, c_n$. That is, $T$ is a complete binary tree with
$c_1,\dots c_n$ in its leaves (in this order) and inner nodes contain the minimum of their
children. Note that each node represents a subpath of~$F$ with leaves being the single
vertices.
......@@ -53,8 +55,8 @@ an interval of leaves of~$T$ and each such interval can be exactly covered by $\
subtrees and they can be easily found by traversing~$T$ top to bottom. The answer to the
path query can then be easily calculated from the values in the roots of these subtrees.
The point update of $w_i$ is simply a matter of recalculating values in the nodes on path
from root of~$T$ to the leaf~$w_i$, so it takes $\O(\log n)$ time.
The point update of $c_i$ is simply a matter of recalculating values in the nodes on path
from root of~$T$ to the leaf~$c_i$, so it takes $\O(\log n)$ time.
The path updates are more tricky. As in the path query, we can decompose the update to
$\O(\log n)$ subtrees. But we cannot afford to recalculate the values in these subtrees
......@@ -88,8 +90,8 @@ up, towards the root \NOTE\foot{maybe unnecessary now}.
\defn{
Let~$F$ be a rooted tree. For any vertex~$v$ we define $s(v)$ to be the size of subtree
rooted at~$v$ (including~$v$). Let~$u$ be a child of~$v$, we say the edge~$(v,u)$ is
\em{heavy} iff $s(u) \ge s(v)/2$, otherwise we say $(v,u)$ is \em{light}. Finally, a
rooted at~$v$ (including~$v$). Let~$u$ be a child of~$v$, we say the edge~$(u,v)$ is
\em{heavy} iff $s(u) \ge s(v)/2$, otherwise we say $(u,v)$ is \em{light}. Finally, a
\em{heavy path} is simply a path containing only heavy edges.
}
......@@ -161,7 +163,7 @@ A data structure based on heavy-light decomposition can perform \em{path queries
$\O(n)$ time and requires $\O(n)$ space.
}
\subsection{Static weights}
\subsection{Static costs}
Let us analyze the partitioning of a path in a bit more detail:
......@@ -170,20 +172,93 @@ When we partition a path into $\O(\log n)$ heavy subpaths, all of the subpaths,
exception, are a prefix or a suffix of heavy path.
}
We can make this observation to make path queries faster but at the cost of keeping the
weights static and forgoing the path updates. For each heavy path we calculate and store
We can use this observation to make path queries faster but at the cost of keeping the
costs static and forgoing the path updates. For each heavy path we calculate and store
prefix and suffix minimums. This allows us to answer almost all subqueries in constant
time and the one remaining subquery can be answered in $\O(\log n)$.
\cor{
On a static tree with static weights, the path queries can be answered in $\O(\log n)$
On a static tree with static costs, the path queries can be answered in $\O(\log n)$
time.
}
\section[linkcut]{Link-cut trees}
\TODO
Link-cut trees are dynamic version of the heavy-light decomposition. They allow us to
change structure of the represented forest by either linking two trees or by cutting an
edge inside a tree. Link-cut trees were introduced in a paper by Sleator and Tarjan \TODO
reference. However, we will show later version that uses splay trees instead of original
biased trees \TODO reference. Although it achieves the time complexity only in amortized
case, it is significantly easier to analyze.
Link-cut tree represents a forest $F$ of \em{rooted} trees; each edge is oriented towards the
respective root. It supports following operations:
\TODO proper formatting
\list{o}
\: Structural queries:
\tightlist{-}
\:$\opdf{Parent}(v)$;
\:$\opdf{Root}(v)$ --- return root of $v$'s tree
\endlist
\: Structural updates:
\tightlist{-}
\:$\opdf{Cut}(v)$ --- remove an edge between $v$ and $\op{Parent}(v)$;
\:$\opdf{Link}(u,v)$ --- create edge $(v,u)$ such that $v$ becomes a child of $u$ ($v$
needs to be a root before the call of $\op{Link}$ and both vertices cannot lie within
the same tree);
\:$\opdf{Evert}(v)$ --- reroot the tree and make $v$ the root
\endlist
\: Cost queries:
\tightlist{-}
\:$\opdf{Cost}(v)$;
\:$\opdf{PathMin}(v)$ --- return minimum cost on the path $v \to \op{Root}(v)$
\endlist
\: Cost updates:
\tightlist{-}
\:$\opdf{SetCost}(v,x)$;
\:$\opdf{PathUpdate(v,\delta)}$ --- increase all costs on the path $v \to
\op{Root}(v)$ by $\delta$.
\endlist
\endlist
Link-cut trees use a similar approach as the heavy-light decomposition and each
tree is decomposed into a system of paths. Instead of heavy and light edges we have
\em{fat} and \em{thin} edges. However, whether an edge is fat or thin is not given by the
structure of the tree but by the history of the data structure. The only requirement is
that every vertex has at most one incoming fat edge. This requirement assures that the
tree can be decomposed into a system of fat paths interconnected by thin edges. Unlike
heavy-light decomposition, we don't have bound on the number of thin edges on the path $v
\to \Root(v)$. In fact, it is possible that there are only thin edges in the tree!
Nevertheless, we will show that everything works out in the amortized case.
The key ingredient of our data structure is the $\opdf{Expose}$ operation. $\Expose(v)$
changes the fat-thin decomposition in such a way that $v$ and $\Root(v)$ are the endpoints
of a single fat path. Internally, all operations on trees are implemented using $\Expose$ and
a constant number of operations on a fat path:
\list{-}
\:$\Root(v)$ = $\Expose(v)$, then return the top of the fat path;
\:$\op{Cut}(v)$ = $\Expose(\Parent(u))$, then remove thin edge;
\:$\op{Link}(u,v)$ = $\Expose(u)$, then join two fat paths;
\:$\op{Evert}(v)$ = $\Expose(v)$, then reverse fat path;
\:$\op{PathMin}(v)$ = $\Expose(v)$, then return path minimum for the fat path;
\:$\op{PathUpdate}(v)$ = $\Expose(v)$, then perform path update on the fat path.
\endlist{}
Conceptually, $\Expose(v)$ is straightforward. Assume~$v$ lies on a fat path~$A$. We start
at~$v$ and jump along~$A$ to its top~$t$. Unless $t$ is the root of the tree (which means
we are done), $t$ is connected to with a fat path~$B$ via thin edge $(t, p)$, see
Figure~\TODO. We cut~$B$ by turning fat edge below~$p$ into a thin edge. Then we join top
half of~$B$ with~$A$ by making edge~$(t, p)$ fat. Now we jump to the top of the newly
created fat path and repeat the whole process. In the end, we turn a fat edge below~$v$
into thin edge (if there was one) and make~$v$ the endpoint of the fat path.
\TODO picture of one step of expose
The actual implementation of $\Expose$ is, however, more tricky, due to our representation
paths and trees.
\subsection[Representation of fat paths via splay trees]
\TODO
\section{Application: Faster Dinic's algorithm}
\TODO
......
<?xml version="1.0"?>
<!DOCTYPE ipe SYSTEM "ipe.dtd">
<ipe version="70212" creator="Ipe 7.2.13">
<info created="D:20210528110052" modified="D:20210602150616"/>
<info created="D:20210528110052" modified="D:20210803140920"/>
<ipestyle name="basic">
<symbol name="arrow/arc(spx)">
<path stroke="sym-stroke" fill="sym-stroke" pen="sym-pen">
......@@ -255,7 +255,7 @@ h
<page>
<layer name="alpha"/>
<view layers="alpha" active="alpha"/>
<text layer="alpha" matrix="1 0 0 1 -80 -192" transformations="translations" pos="176 656" stroke="black" type="label" width="11.602" height="4.294" depth="1.49" halign="center" valign="baseline" style="math">w_1</text>
<text layer="alpha" matrix="1 0 0 1 -80 -192" transformations="translations" pos="176 656" stroke="black" type="label" width="8.781" height="4.294" depth="1.49" halign="center" valign="baseline" style="math">c_1</text>
<use matrix="1 0 0 1 -80 -192" name="mark/disk(sx)" pos="288 768" size="normal" stroke="black"/>
<use matrix="1 0 0 1 -80 -192" name="mark/disk(sx)" pos="224 736" size="normal" stroke="black"/>
<use matrix="1 0 0 1 -80 -192" name="mark/disk(sx)" pos="352 736" size="normal" stroke="black"/>
......@@ -371,13 +371,13 @@ h
<text matrix="1 0 0 1 -80 -192" transformations="translations" pos="336 624" stroke="black" type="label" width="4.981" height="6.42" depth="0" halign="center" valign="center">6</text>
<text matrix="1 0 0 1 -80 -192" transformations="translations" pos="368 624" stroke="black" type="label" width="4.981" height="6.42" depth="0" halign="center" valign="center">7</text>
<text matrix="1 0 0 1 -80 -192" transformations="translations" pos="400 624" stroke="black" type="label" width="4.981" height="6.42" depth="0" halign="center" valign="center">8</text>
<text matrix="1 0 0 1 -48 -192" transformations="translations" pos="176 656" stroke="black" type="label" width="11.602" height="4.294" depth="1.49" halign="center" valign="baseline" style="math">w_2</text>
<text matrix="1 0 0 1 -16 -192" transformations="translations" pos="176 656" stroke="black" type="label" width="11.602" height="4.294" depth="1.49" halign="center" valign="baseline" style="math">w_3</text>
<text matrix="1 0 0 1 16 -192" transformations="translations" pos="176 656" stroke="black" type="label" width="11.602" height="4.294" depth="1.49" halign="center" valign="baseline" style="math">w_4</text>
<text matrix="1 0 0 1 48 -192" transformations="translations" pos="176 656" stroke="black" type="label" width="11.602" height="4.294" depth="1.49" halign="center" valign="baseline" style="math">w_5</text>
<text matrix="1 0 0 1 80 -192" transformations="translations" pos="176 656" stroke="black" type="label" width="11.602" height="4.294" depth="1.49" halign="center" valign="baseline" style="math">w_6</text>
<text matrix="1 0 0 1 112 -192" transformations="translations" pos="176 656" stroke="black" type="label" width="11.602" height="4.294" depth="1.49" halign="center" valign="baseline" style="math">w_7</text>
<text matrix="1 0 0 1 144 -192" transformations="translations" pos="176 656" stroke="black" type="label" width="11.602" height="4.294" depth="1.49" halign="center" valign="baseline" style="math">w_8</text>
<text matrix="1 0 0 1 -48 -192" transformations="translations" pos="176 656" stroke="black" type="label" width="8.781" height="4.294" depth="1.49" halign="center" valign="baseline" style="math">c_2</text>
<text matrix="1 0 0 1 -16 -192" transformations="translations" pos="176 656" stroke="black" type="label" width="8.781" height="4.294" depth="1.49" halign="center" valign="baseline" style="math">c_3</text>
<text matrix="1 0 0 1 16 -192" transformations="translations" pos="176 656" stroke="black" type="label" width="8.781" height="4.294" depth="1.49" halign="center" valign="baseline" style="math">c_4</text>
<text matrix="1 0 0 1 48 -192" transformations="translations" pos="176 656" stroke="black" type="label" width="8.781" height="4.294" depth="1.49" halign="center" valign="baseline" style="math">c_5</text>
<text matrix="1 0 0 1 80 -192" transformations="translations" pos="176 656" stroke="black" type="label" width="8.781" height="4.294" depth="1.49" halign="center" valign="baseline" style="math">c_6</text>
<text matrix="1 0 0 1 112 -192" transformations="translations" pos="176 656" stroke="black" type="label" width="8.781" height="4.294" depth="1.49" halign="center" valign="baseline" style="math">c_7</text>
<text matrix="1 0 0 1 144 -192" transformations="translations" pos="176 656" stroke="black" type="label" width="8.781" height="4.294" depth="1.49" halign="center" valign="baseline" style="math">c_8</text>
<path stroke="black">
176 520 m
152 472 l
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment