Задача о динамической связности — различия между версиями
 (→Псевдокод)  | 
				 (→Псевдокод)  | 
				||
| Строка 46: | Строка 46: | ||
     '''Edge''' e = <tex>\langle </tex>u, v<tex>\rangle</tex>  |      '''Edge''' e = <tex>\langle </tex>u, v<tex>\rangle</tex>  | ||
     e.level = 0  |      e.level = 0  | ||
| − |      <tex>G_0</tex> = <tex>G_0</tex> <tex>\cup</tex>e<!---insert(<tex>G_0</tex>, e)-->  | + |      <tex>G_0</tex> = <tex>G_0</tex> <tex>\cup</tex> e<!---insert(<tex>G_0</tex>, e)-->  | 
     '''if not''' <tex>\mathrm{connected(u,v)}</tex>  |      '''if not''' <tex>\mathrm{connected(u,v)}</tex>  | ||
       <tex>F_0</tex> = <tex>F_0</tex> <tex>\cup</tex> e<!---insert(<tex>F_0</tex>, e)-->  |        <tex>F_0</tex> = <tex>F_0</tex> <tex>\cup</tex> e<!---insert(<tex>F_0</tex>, e)-->  | ||
Версия 00:12, 20 января 2018
| Задача: | 
Есть неориентированный граф из  вершин, изначально не содержащий рёбер. Требуется обработать  запросов трёх типов:
  | 
Содержание
Динамическая связность в лесах
Если задача такова, что в графе нет и не может быть циклов, то она сводится к задаче о связности в деревьях эйлерова обхода. Время работы каждого запроса для упрощённой задачи — , где — количество вершин в графе.
Обобщение задачи для произвольных графов
Существуют задачи, в которых граф не обязательно на протяжении нашей работы после каждой операции добавления ребра остаётся лесом. Для решения таких задач в каждой компоненте связности выделим остовные деревья, которые образуют остовный лес.
Проверка связности
Граф и его остовный лес — одно и то же с точки зрения связности. Поэтому проверка связности в графе сводится к проверке связности в остовном лесе и решается за .
Добавление ребра
Чтобы разобраться с тем, как изменится граф и остовный лес при добавлении и удалении ребра, введём функцию и назовём её уровнем ребра . Уровни ребра можно распределить любым способом, но для всех должно выполняться следующее свойство: размер каждой компоненты связности не превосходит . Здесь графы определяются так: .
Очевидно, что . Выделим в графах остовные леса таким образом, что , где — остовный лес графа .
Удобнее всего новому ребру давать уровень . В этом случае изменится только , так как в остальные подграфы рёбра нулевого уровня не входят. После вставки нового ребра нам нужно проверить, были ли вершины и в одной компоненте связности до того, как мы вставили ребро. Если они лежали в разных компонентах, то необходимо новое ребро добавить и в остовный лес .
Псевдокод
function (Node u, Node v): Edge e = u, v e.level = 0 = e if not = e
Удаление ребра
| Утверждение: | 
Если ребро, которое мы хотим удалить, не принадлежит остовному лесу, то связность между любой парой вершин сохранится.  | 
|  
 Докажем от противного. Допустим, что это не так. Понятно, что при разрезании ребра нового пути между вершинами не появится. Предположим, что нарушилась связность для каких-то двух вершин. Значит, мы убрали мост. А любой мост принадлежит всем остовным деревьям его компоненты. Противоречие. | 
Таким образом, если мы удалили ребро не из остовного леса, то нам не придётся перестраивать лес и пересчитывать значение .
Рассмотрим случаи, когда мы берём ребро из леса. Тогда необходимо выяснить, является ли данное ребро мостом в графе, и выполнить соответствующие действия.
Проверим, является ли ребро мостом. У ребра известен уровень, пусть он равен . Попробуем найти другое ребро (), соединяющее поддеревья и , на которые распалось остовное дерево исследуемой компоненты .
| Утверждение: | 
Если ребро  существует, то его уровень не больше .  | 
| От противного. Пусть , где . Тогда вершины и каким-то образом связаны в (либо непосредственно ребром , либо каким-то другим путём). Но . Значит, в между и сохранился путь из рёбер уровня не меньше и появился другой путь через . Приходим к противоречию, так как в все компоненты должны быть деревьями. | 
Чтобы найти , выберем из поддеревьев и наименьшее. Не умаляя общности, будем считать, что . Так как всегда из двух слагаемых можно выбрать одно такое, что оно не превосходит половины их суммы, имеем важное свойство: . Также нам известно, что , а значит, . Отсюда . Это неравенство позволит нам увеличивать уровни рёбер при необходимости.
Будем искать ребро следующим образом:
- Выбираем любое ребро уровня , выходящее из вершины, принадлежащей .
 - Если выбранное ребро ведёт в , выходим из цикла и добавляем ребро в остовные леса , для которых и выходим из цикла;
 - Если выбранное ребро ведёт в другую вершину поддерева , увеличиваем его уровень на ;
 - Если есть непроверенные рёбра на интересующем нас уровне , переходим к пункту ;
 - Если таких рёбер уровня не осталось и , рассматриваем уровень на единицу меньший и переходим к пункту ;
 - Если все рёбра просканированы и , то является мостом.
 
Замечание. Увеличив уровень ребра на единицу, нужно не забыть обновить и .
Оценка времени работы
Пункт работает за , так как после выхода из цикла мы добавляем ребро за на каждом уровне, а количество уровней не больше .
Пусть до момента, когда мы нашли нужное ребро, мы сделали неудачных сканирований. После каждого такого сканирования нам приходится добавлять новые рёбра в , что стоит . Получаем сложность удаления одного ребра .
Выразим сложность одной операции другим способом. Для вершин и вызовов процедуры сложность равна , что не превосходит , так как уровень ребра раз рос максимум до . Отсюда суммарная сложность всех запросов равна , а для одного запроса мы решаем задачу за .
Псевдокод
function (Node u, Node v): Edge e = u, v i = e.level while i 0 = e = e Edge e2 for e2 = x, y : e2.level == i and x if y while i 0 = e2 i-- return else e2.level++ = e2 i--