Задача о динамической связности — различия между версиями
(→Псевдокод) |
(→remove(u,v)) |
||
Строка 71: | Строка 71: | ||
Попробуем найти подходящую вершину <tex>x</tex> в <tex>T_u</tex> следующим образом: | Попробуем найти подходящую вершину <tex>x</tex> в <tex>T_u</tex> следующим образом: | ||
− | # Если исходящее ребро ведёт в <tex>T_v</tex>, то добавляем ребро <tex>xy</tex> в остовные леса <tex>F_i</tex>, для которых <tex>i\leqslant l(xy)</tex> и выходим из цикла; | + | # Если исходящее ребро ведёт в <tex>T_v</tex>, то выходим из цикла и добавляем ребро <tex>xy</tex> в остовные леса <tex>F_i</tex>, для которых <tex>i\leqslant l(xy)</tex> и выходим из цикла; |
# Если исходящее ребро ведёт в другую вершину поддерева <tex>T_u</tex>, увеличиваем его уровень на <tex>1</tex>; | # Если исходящее ребро ведёт в другую вершину поддерева <tex>T_u</tex>, увеличиваем его уровень на <tex>1</tex>; | ||
# Если есть непроверенные рёбра, переходим к пункту <tex>1</tex>; | # Если есть непроверенные рёбра, переходим к пункту <tex>1</tex>; | ||
Строка 78: | Строка 78: | ||
'''Замечание.''' Увеличив уровень ребра на единицу, нужно не забыть обновить <tex>G_{i+1}</tex> и <tex>F_{i+1}</tex>. | '''Замечание.''' Увеличив уровень ребра на единицу, нужно не забыть обновить <tex>G_{i+1}</tex> и <tex>F_{i+1}</tex>. | ||
+ | ====Оценка времени работы==== | ||
+ | Пункт <tex>1</tex> работает за <tex>O(\log^2 n)</tex>, так как мы добавляем ребро на каждом уровне, а количество уровней не больше <tex>\log n</tex>. | ||
+ | |||
+ | Пункт <tex>2</tex> выполняется за <tex>O(\log n)</tex> и вызывается до <tex>\log n</tex> раз. | ||
+ | |||
+ | Пусть до момента, когда мы нашли нужное ребро, мы сделали <tex>S</tex> неудачных сканирований. Получаем сложность удаления одного ребра <tex>O(\log^2{n}+S\cdot\log n)</tex>. Для <tex>m</tex> вызовов процедуры <tex>\mathrm{remove(u, v)}</tex> сложность равна <tex>O(\log^2{n}\cdot m+\mathrm{\log}n\cdot\sum{S})</tex>, что не превосходит O(\log^2{n} \cdot m+\log n\cdot\log n\cdot m). Отсюда суммарная сложность всех запросов равна O(\log^2{n}\cdot m)</tex>, а для одного запроса мы решаем задачу за <tex>O(\log^2{n}</tex> | ||
====Псевдокод==== | ====Псевдокод==== | ||
− | '''while''' i >= 0 | + | |
− | + | '''function''' remove ('''Node''' u, '''Node''' v): | |
− | + | '''while''' i >= 0 | |
− | + | e = <x, y> | |
− | + | '''for''' y : e.level == i | |
− | + | '''if''' y <tex>\in T_v</tex> | |
+ | '''for''' j = i '''downto''' 0 | ||
+ | insert(<tex>F_j</tex>, e) | ||
'''break''' | '''break''' | ||
− | + | '''else''' e.level++ | |
− | + | i-- | |
<!----При удалении возможны случаи: | <!----При удалении возможны случаи: |
Версия 00:23, 15 января 2018
Задача: |
Есть неориентированный граф из вершин, изначально не содержащий рёбер. Требуется обработать запросов трёх типов:
|
Содержание
Динамическая связность в лесах
Если задача такова, что в графе нет и не может быть циклов, то она сводится к задаче о связности в деревьях эйлерова обхода. Время работы каждого запроса для упрощённой задачи — .
Обобщение задачи для произвольных графов
Существуют задачи, в которых граф не обязательно на протяжении нашей работы после каждой операции добавления ребра остаётся лесом. Для решения таких задач в каждой компоненте связности выделим остовные деревья, которые образуют остовный лес. Попробуем выполнить операцию удаления ребра.
connected(u,v)
Граф и его остовный лес — одно и то же с точки зрения связности. Поэтому проверка связности в графе сводится к проверке связности в остовном лесе и решается за
.add(u,v)
Чтобы разобраться с тем, как изменится граф и остовный лес при добавлении и удалении ребра, введём функцию
и назовём её уровнем ребра . Уровни ребра можно распределить любым способом, но для всех должно выполняться следующее свойство: размер каждой компоненты связности не превосходит . Здесь графы определяются так: .Очевидно, что
. Выделим в графах остовные леса таким образом, что , где — остовный лес графа .Удобнее всего новому ребру давать уровень
. В этом случае изменится только , так как в остальные подграфы рёбра нулевого уровня не входят. После вставки нового ребра нам нужно проверить, были ли вершины и в одной компоненте связности до того, как мы вставили ребро. Если они лежали в разных компонентах, то необходимо новое ребро добавить и в остовный лес .Псевдокод
function add (Node u, Node v): e = <x, y> e.level = 0 insert(, e) if not connected(u, v) insert( , e)
remove(u,v)
Утверждение: |
Если ребро, которое мы хотим удалить, не принадлежит остовному лесу, то связность между любой парой вершин сохранится. |
Докажем от противного. Допустим, что это не так. Понятно, что при разрезании ребра нового пути между вершинами не появится. Предположим, что нарушилась связность для каких-то двух вершин. Значит, мы убрали мост. А любой мост принадлежит всем остовным деревьям его компоненты. Противоречие. |
Таким образом, если мы удалили ребро не из остовного леса, то нам не придётся перестраивать лес и пересчитывать значение
. Рассмотрим случаи, когда мы берём ребро из леса. Тогда необходимо выяснить, не является ли данное ребро мостом в графе, и выполнить соответствующие действия.Проверим, является ли ребро мостом. У ребра
известен уровень, пусть он равен . Попробуем найти другое ребро ( ), соединяющее поддеревья и , на которые распалось остовное дерево исследуемой компоненты .Утверждение: |
От противного. Пусть | и . Тогда вершины и каким-то образом связаны в (либо непосредственно ребром , либо каким-то другим путём). Но . Значит, в между и сохранился путь из рёбер уровня не меньше и появился другой путь через . Приходим к противоречию, так как в все компоненты должны быть деревьями.
Чтобы найти
, выберем из поддеревьев и наименьшее. Не умаляя общности, будем считать, что . Так как хотя бы одно из двух слагаемых всегда не превосходит половины их суммы, имеем важное свойство: . Также нам известно, что , а значит, . Отсюда . Это неравенство позволит нам увеличивать уровни рёбер при необходимости.Попробуем найти подходящую вершину
в следующим образом:- Если исходящее ребро ведёт в , то выходим из цикла и добавляем ребро в остовные леса , для которых и выходим из цикла;
- Если исходящее ребро ведёт в другую вершину поддерева , увеличиваем его уровень на ;
- Если есть непроверенные рёбра, переходим к пункту ;
- Если таких рёбер уровня не осталось и , уменьшаем уровень на единицу и переходим к пункту ;
- Если все рёбра просканированы и , то является мостом.
Замечание. Увеличив уровень ребра на единицу, нужно не забыть обновить
и .Оценка времени работы
Пункт
работает за , так как мы добавляем ребро на каждом уровне, а количество уровней не больше .Пункт
выполняется за и вызывается до раз.Пусть до момента, когда мы нашли нужное ребро, мы сделали
неудачных сканирований. Получаем сложность удаления одного ребра . Для вызовов процедуры сложность равна , что не превосходит O(\log^2{n} \cdot m+\log n\cdot\log n\cdot m). Отсюда суммарная сложность всех запросов равна O(\log^2{n}\cdot m)</tex>, а для одного запроса мы решаем задачу заПсевдокод
function remove (Node u, Node v): while i >= 0 e = <x, y> for y : e.level == i if yfor j = i downto 0 insert( , e) break else e.level++ i--