Skip to content
28 changes: 14 additions & 14 deletions content/russian/cs/spanning-trees/dcp.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
---
title: Динамическая связность
authors:
- Сергей Слотин
date: 2021-09-25
- Сергей Слотин
date: 2021-09-25T00:00:00.000Z
prerequisites:
- /cs/set-structures/dsu
- /cs/persistent/persistent-array
- /cs/decomposition/rollback
- /cs/set-structures/dsu
- /cs/persistent/persistent-array
- /cs/decomposition/rollback
---

В контексте графов, система непересекающихся множеств напрямую решает следующую задачу:
Expand Down Expand Up @@ -37,17 +37,17 @@ prerequisites:

### Divide-and-conquer по запросам

Давайте вместо корневой эвристики заведем рекурсивную функцию `solve(l, r)`, которая будет отвечать на все запросы с $l$ по $r$, имея СНМ, соответствующий всем ребрам, которые существуют на всем этом промежутке.
Давайте вместо корневой эвристики заведём дерево отрезков, в вершинах которого сохраним все рёбра, существующие целиком на всём отрезке этой вершины и не существующие целиком на отрезке её предка. Таким образом каждое ребро окажется не более чем в $log(n)$ вершинах ДО.

Эта функция будет действовать следующим образом:
Рёбра, имеющие несколько отрезков жизни можно считать разными рёбрами и добавлять в ДО независимо, т. к. эти отрезки не пересекаются, а также их суммарное количество будет не более $n$.

0. Если в промежутке всего один запрос, то найдем ответ на него через СНМ и выйдем. В противном случае:
1. Разделим промежуток времени пополам: `t = (l + r / 2)`.
2. Рекурсивно разрешим левую половину: `solve(l, t)`.
3. Добавим в СНМ те ребра, которые существуют на всей правой половине запросов.
4. Рекурсивно запустимся от правой половины: `solve(t, r)`.
5. Откатим СНМ до изначального состояния.
Чтобы найти ответ, можно пройтись по дереву отрезков такой `dfs(v)`:

Так как мы всегда поддерживаем инвариант «когда мы запускаемся и выходим из рекурсии, СНМ всегда чистый для этого промежутка», алгоритм действительно ответит на все запросы и будет работать за $O(n \log^2 n)$.
0. Добавим в СНМ рёбра из тек. вершины ДО $v$.
1.0. Если отрезок единичной длины, ответим на все его запросы,
1.1. Иначе запустимся в левого, а затем и в правого ребёнка.
2. Откатим все добавленные из тек. вершины $v$ в СНМ рёбра.

Несложно заметить, что асимптотика алгоритма $O(n \log^2 n)$, потому что каждое из не более $n$ рёбер мы добавим в не более чем $log(n)$ отрезком ДО, в каждый отрезок зайдём ровно по 1 разу и каждое ребро из него добавим в СНМ за $O(log(n))$.

Заметим, что мы нигде не использовали ничего конкретно про связность — можно отвечать на любые запросы, поддерживаемые СНМ, например о размерах компонент или числе ребер. Также существуют модификации для других задач, например для нахождения мостов или компонент двусвязности — подробнее можно почитать в [дипломной работе](http://se.math.spbu.ru/SE/diploma/2012/s/Kopeliovich_diploma.pdf) Сергея Копелиовича.