Загрузил rodzher.zet

Практика по алгоритмам DSU, MST

Реклама
Первый курс, весенний семестр 2019/20
Практика по алгоритмам #7
DSU, MST
28 февраля
Собрано 3 марта 2020 г. в 03:05
Содержание
1. DSU, MST
2
2. Разбор задач практики
3
3. Домашнее задание
3.1. Обязательная часть . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.2. Дополнительная часть . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
7
7
8
4. Разбор домашнего задания
4.1. Обязательная часть . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.2. Дополнительная часть . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
9
9
11
Алгоритмы, весна 2019/20
Практика #7. DSU, MST.
DSU, MST
1. Online двудольность
Дан неорграф. В него в online добавляются рёбра.
После каждого добавления говорить, двудолен ли граф?
a) 𝒪(𝑚 log 𝑛).
b) Быстрее.
2. Четность
В каждой клетке прямой записано число 0 или 1. Поступает информация: четность числа
единиц на отрезке [𝐿𝑖 , 𝑅𝑖 ], найти первый запрос, после которого данные противоречивы.
3. Единственность MST
a) Доказать, что в графе с различными весами рёбер остовное дерево единственно.
b) Проверить, что минимальное по весу остовное дерево единственно.
4. Перестроение MST
Дан взвешенный граф 𝐺. Дано минимальное остовное дерево на нем.
У ребра 𝑒 поменяли вес. Найти новое минимальное остовное дерево за 𝒪(𝑉 + 𝐸).
5. Второе по весу ST
Найти второе по весу остовное дерево.
6. Тест для Борувки
Постройте пример для алгоритма Борувки, на котором он делает
a) Θ(log 𝑛) фаз.
b) Θ(1) фаз.
7. (*) Ориентированное MST
Дан орграф, постройте остовное дерево с корнем в вершине 1 минимального веса. Все ребра
в нем должны быть направлены от предков к потомкам.
8. (*) 𝑘-е по весу ST
9. (*) MST через 𝑘 ближайших
Рассмотрим алгоритм построения остовного дерева на плоскости: найдем к каждой точке
𝑘 ближайших, на полученном графе за 𝒪(𝑛𝑘 log 𝑛) найдем минимальное остовное дерево.
Постройте контрпример.
10. (*) Random MST через 𝑘 ближайших
А для точек равномерно распределённых внутри [0..109 ] × [0..109 ] этот алгоритм отлично
работает. Почему? Для какого 𝑘 его запускать?
2/12
Закончилась практика?
Отнеси лист на второй этаж в эко-мусорку
Алгоритмы, весна 2019/20
Практика #7. DSU, MST.
Разбор задач практики
1. Online двудольность
a) 𝒪(𝑚 + 𝑛 log 𝑛) на все запросы. Если ребро соединяет вершины одной компоненты, и его
концы одного цвета, то граф отныне и во веки веков не двудолен, есть нечетный цикл.
Если разного, то всё хорошо.
Если ребро соединяет вершины разных компонент, то при разных цветах всё хорошо, при
одинаковых перекрашиваем все вершины меньшей компоненты.
Для каждой вершины храним номер компоненты, в которой она лежит, при соединении
меняем номера в меньшей компоненте.
Тогда каждая вершина поменяет свой номер и цвет не более 𝒪(log 𝑛) раз.
b) 𝒪((𝑚 + 𝑛)𝛼(𝑛)), способ 1. Храним компоненты связности с DSU, перекрашиваем лениво.
Реализация: на рёбрах DSU указаны цвета 0 и 1. Цвет вершины – XOR значений на пути
до корня DSU.
При join ставим на новое ребро 1, если нужно перекрашивание.
При сжатии путей на новое ребро ставим цвет, равный цвету вершины.
𝒪((𝑚 + 𝑛)𝛼(𝑛)), способ 2. У каждой компоненты связности есть одна или две доли.
Храним доли в DSU. Если у компоненты две доли, их корни 𝑎𝑖 , 𝑏𝑖 , то будем хранить
«ссылку на вторую долю» link[a𝑖 ] = b𝑖 , link[b𝑖 ] = a𝑖 . При добавлении ребра нужно
пересчитать DSU и link.
2. Четность
Если число единиц на отрезке [𝐿, 𝑅] четно, то число единиц на префиксах [1, 𝐿−1] и [1, 𝑅]
имеет одинаковую четность, если нечетно – разную.
То есть задача аналогична предыдущей, только здесь нам собщают либо что концы ребра
должны иметь одинаковый цвет, либо что разный. Отрезок [𝐿, 𝑅] – ребро между вершинами
𝐿−1 и 𝑅.
При больших координатах в offline можно сжать координаты, в online использовать хештаблицу (если p[x] еще не был определен, то p[x] = x).
3. Единственность MST
Простой способ. Строим как-нибудь MST 𝑇 .
Каждому ребру 𝑒 даем вес-пару ⟨𝑤𝑒 , 𝑏⟩. Здесь 𝑏 = 1, если 𝑒 ∈ 𝑇 , иначе 0.
На этом запускаем Краскала. Ребра из 𝑇 теперь позже в сортировке, мы понизили их приоритет.
Если новое дерево равно 𝑇 , то единственно.
Способ с понятным док-вом. Запускаем алгоритм Краскала.
Для этого сортируем ребра по весу и обрабатываем ребра группами одинакового веса.
Посмотрим на все ребра веса 𝑤. Пройдем по ним и пометим те, которые сейчас соединяют
разные компоненты (образованные меньшими ребрами), но в дерево пока не добавим.
Сделаем по помеченным ребрам второй проход, но уже объединяя компоненты и добавляя
ребра в остовное дерево.
Если какое-то помеченное ребро 𝑒𝑖 не добавилось в дерево, значит, его добавление создает
цикл. Раз пометили 𝑒𝑖 на первом проходе, раньше оно не создавало цикла ⇒ на цикле есть
ребро 𝑒𝑗 веса 𝑤 ⇒ MST не единственно, можно заменить 𝑒𝑖 на 𝑒𝑗 .
Если ни разу не возникла такая ситуация, MST единственно.
3/12
Алгоритмы, весна 2019/20
Практика #7. DSU, MST.
4. Перестроение MST
Пусть наше минимальное дерево 𝑇 .
Если уменьшилось ребро из 𝑇 , или увеличилось вне 𝑇 , то ничего не изменилось: как
было любое другое дерево не лучше 𝑇 , так и осталось.
Пусть увеличилось 𝑒 из 𝑇 . Удалим его. Добавим минимальное ребро, соединяющее разные
компоненты распавшегося 𝑇 (им может оказаться снова 𝑒).
Время 𝒪(𝐸): раскрасить компоненты и перебрать ребра.
Корректность из леммы о разрезе.
Пусть уменьшилось 𝑒 вне 𝑇 . Посмотрим на цикл, который образует 𝑒, если в нём максимальное ребро 𝑓 больше 𝑒, заменим его на 𝑒.
Время работы = времени поиска максимума на пути.
Корректность. Рассмотрим шаги Краскала.
Все ребра, меньшие 𝑒 или большие 𝑓 распределятся, как и раньше, с их точки зрения компоненты те же.
𝑒 будет взято, ибо соединяет пока разные компоненты 𝐴 и 𝐵.
Ребра между 𝑒 и 𝑓 , которые раньше не брали, не соединяют 𝐴 и 𝐵 (иначе 𝑓 дало бы цикл)
⇒ не возьмем и теперь.
Ребра между 𝑒 и 𝑓 , которые раньше брали, возьмем и сейчас (компонент сейчас только меньше).
𝑓 не взято, образует цикл с 𝑒.
5. Второе по весу ST
Найдём MST 𝑇 . Второе по весу ST 𝑇2 с ним не совпадает, значит, есть ребро 𝑒 в 𝑇2 , но не в
𝑇 , переберем его.
𝑒 создает цикл с путем, соединяющим его концы в 𝑇 . Выкинем из пути ребро max веса,
добавим 𝑒. Получили кандидата на 𝑇2 . Из всех кандидатов выберем минимум.
Искать максимальный вес на пути можно за 𝒪(𝑛), тогда получим алгоритм за 𝒪(𝑚𝑛). Есть
более быстрые способы искать максимум на пути, в offline можно даже за 𝒪(1).
Корректность. Нас интересует минимальное остовное дерево, содержащее 𝑒. Если вычесть
из из веса 𝑒 большое число, то оно точно войдет в MST. Заметим, что мы действовали ровно
так же, как в задаче про уменьшение веса ребра, коректность этого уже доказана.
6. Тест для Борувки
a) Θ(log 𝑛) фаз. Бьем вершины на пары, проводим в парах ребра веса 1. Полученные компоненты соединяем ребром веса 2, и так далее. То есть граф размера 2𝑘 с 𝑘 фазами строится
рекурсивно: соединим ребром веса 𝑘 два графа размера 2𝑘−1 .
b) Θ(1) фаз.
Еще можно сделать бамбук с одинаковыми весами, в котором вершины пронумерованы
4/12
Алгоритмы, весна 2019/20
Практика #7. DSU, MST.
по порядку. Тогда за счет сравнения пар тоже сработает за одну фазу.
Ещё можно расставить веса в произвольном графе: возьмем любое остовное дерево и
поставим вес 𝑖 ребрам между вершинами глубины (𝑖 − 1) и глубины 𝑖. На ребрах не из
дерева вес ∞.
7. (*) Ориентированное MST
Отличный текст от Олега Давыдова.
Пусть все вершины достижимы из корня, иначе сразу ясно, что решения нет.
Наблюдение: если взять ∀𝑣 ̸= root и вычесть из всех входящих в нее ребер число 𝑥, то вес
любого дерева изменится на 𝑥, так как в 𝑣 входит только одно ребро дерева. Значит, MST
останется тем же.
∀𝑣 ̸= root находим 𝑥𝑣 = min 𝑤(𝑢, 𝑣) и вычитаем 𝑥𝑣 из всех входящих в 𝑣 ребер.
Все ребра стали > 0, и в каждую вершину 𝑣 входит хотя бы одно нулевое 𝑒𝑣 .
Если теперь есть дерево из нулей, оно – ответ.
Иначе есть цикл из нулей: из какой-то 𝑣 не смогли дойти до корня по нулевым ребрам, значит, зациклились.
Сожмем цикл и рекурсивно запустим алгоритм на новом графе.
Получили ответ для графа, где некоторые вершины – сжатые циклы. Эти циклы нужно
расжать. При расжатие цикла в остовное дерево добавляются все ребра цикла, кроме входящего в вершину, у которой уже есть входящее ребро.
Время 𝒪(𝑉 𝐸): каждая фаза за 𝒪(𝐸), в каждой фазе хотя бы на одну вершину стало меньше,
то есть 6 𝑉 фаз.
8. (*) 𝑘-е по весу ST
Йен, будем класть в кучу кандидатов и то, к каким «классам» они относятся.
Сначала другой способ получения второго: перебираем, какое ребро запретить, т.е. какое
есть в 𝑇 , но нет в 𝑇2 .
При запрете ребра получаем разрез дерева, находим min ребро через него.
Итого 𝒪(𝑛𝑚): для каждого из (𝑛 − 1) ребра остова ищем ему замену за 𝒪(𝑚).
Что задает «класс» решений? Множество запрещенных ребер. Итого 𝒪(𝑘𝑛𝑚).
9. (*) MST через 𝑘 ближайших
Пример для 𝑘 < 𝑛2 : 𝑛2 точек вокруг (0, 0) и 𝑛2 точек вокруг (109 , 0). Остов выйдет несвязным.
Можно ещё построить пример, в котором мы возьмём лишнее ребро при 𝑘 < 𝑛2 − 1:
5/12
Алгоритмы, весна 2019/20
Практика #7. DSU, MST.
10. (*) Random MST через 𝑘 ближайших
Чем 𝑘 больше, тем меньше вероятности ошибки. Авторское решение задачи из контеста
использует 𝑘 = 13.
Обозначим сторону квадрата за 𝑀 . Квадрат со стороной 𝑚 =
квадрат 𝑀 × 𝑀 разбивается на 𝑛 единичных квадратов.
Единичный квадрат пуст с вероятностью (1 − 𝑛1 )𝑛 → 𝑒−1 .
𝑀
√
𝑛
назовём единичным. Весь
𝑛→∞
2
Квадрат со стороной 𝑡𝑚 не пуст с вероятностью ≈ 1 − 𝑒−𝑡 .
2 𝑛
Все 𝑡𝑛2 квадратов со стороной 𝑡𝑚 не пусты с вероятностью (1 − 𝑒−𝑡 ) 𝑡2 .
𝑛
√
При 𝑡 = 2 ln 𝑛 получаем (1− 𝑛1 ) ln2 𝑛 → 1. Т.е., почти наверняка все квадраты такого рамера
𝑛→∞
содержат хотя бы одну точку.
Пользуемся леммой о разрезе. Рассмотрим произвольный разрез.
Если в каком-то
√ из квадратов 𝑡𝑚 × 𝑡𝑚 есть точки из разных частей разреза, расстояние
между ними 6 2𝑡𝑚.
Иначе есть два соседних
квадрата, которые лежат в разных частях разреза, расстояние
√
между точками 6 5𝑡𝑚.
√
То есть, длина ребра, которое нужно добавить в MST не более 𝑟 = 5𝑡𝑚.
Посмотрим, сколько точек может лежать в круге радиуса 𝑟. Площадь круга равна 5𝜋𝑡2 𝑚2 =
20𝜋(ln 𝑛)𝑚2 . Матожидание числа точек в этом круге 𝐸 = 20𝜋(ln 𝑛).
Вероятность того, что туда попадёт более 40𝜋 ln 𝑛 точек, незначительна. Поэтому
нам точно хватит 𝑘 = 40𝜋 ln 𝑛.
6/12
Алгоритмы, весна 2019/20
Практика #7. DSU, MST.
Домашнее задание
3.1. Обязательная часть
1. (1) Ремонт дорог
Дана страна, состоящая из городов и двусторонних дорог. Каждая дорога или в рабочем
состоянии, или требует ремонта стоимости 𝑤𝑖 . Ездить можно только по дорогам в рабочем
состоянии. За минимальную стоимость отремонтировать некоторые дороги так, чтобы из
любого города страны можно было добраться в любой другой.
2. (2) Проверка на минимальность
Пусть мы умеем искать минимум на пути в дереве за время 𝑀 (𝑛). Дано остовное дерево.
За сколько можно проверить то, что оно является минимальным по весу? Доказать.
3. (3) Минимум на пути
Дано корневое дерево из 𝑛 вершин. Все ребра ориентированы к корню, на них есть веса.
Путь называется вертикальным, если его вершина-конец является предком вершины-начала.
Даны 𝑚 вертикальных путей, за 𝒪((𝑛+𝑚) log 𝑛) времени и 𝒪(𝑛+𝑚) дополнительной памяти
найти минимум на каждом из путей.
Подсказка: сперва на 2 балла решите такую же задачу для массива (массив – частный
случай дерева!). Чтобы массив был больше похож на дерево, нарисуйте вертикально. Может пригодиться СНМ.
Дерево отрезков использовать нельзя.
4. (2) Кратчайший путь
За 𝒪(𝐸 log 𝑉 ) сделать предподсчёт, а затем для любой пары вершин за 𝒪(размера ответа)
возвращать путь между 𝑎𝑖 и 𝑏𝑖 , в котором минимальный вес ребра максимален.
5. (2) Random и СНМ
Рассмотрим реализацию СНМ:
int get( int v ) { return v == p[v] ? v : get(p[v]); }
void join( int a, int b ) {
a = get(a), b = get(b);
if (rand() & 1) swap(a, b);
p[a] = b;
}
За сколько в худшем случае работает такая реализация?
Докажите оценку сверху, приведите тест, на котором она достигается.
(+1) За оценку того же кода с get со сжатием путей.
7/12
Алгоритмы, весна 2019/20
Практика #7. DSU, MST.
3.2. Дополнительная часть
1. (3) Dynamic connectivity in directed graph
Дан ацикличный орграф. Нужно за 𝒪(𝑛2 ) обрабатывать запросы «добавить ребор» и «удалить ребро». Гарантируется, что граф всегда остается ацикличным. Также нужно за 𝒪(1)
отвечать на запрос «есть ли путь из 𝑎 в 𝑏»?
Подсказка: наш алгоритм будет вероятностным.
2. (3) Dynamic 2-connectivity
В неорграф добавляются ребра. Нужно после каждого запроса добавления говорить, сколько
в графе мостов. Подсказка: число мостов – число рёбер в дереве рёберной двухсвязности.
3. (3) Одного сжатия путей мало...
Построить тест, на котором 𝑚 запросов к СНМ-у с одной эвристикой сжатия путей работают
за Ω(𝑚 log 𝑛). Можете делать любые запросы. 𝑚 = Θ(𝑛).
4. (3) Euclidean MST
Даны 𝑛 произвольных точек на плоскости, построить MST за 𝒪(𝑛 log 𝑛).
8/12
Алгоритмы, весна 2019/20
Практика #7. DSU, MST.
Разбор домашнего задания
4.1. Обязательная часть
1. Ремонт дорог
Сделаем стоимость целых дорог нулевой, построим MST.
2. Проверка на минимальность
Если умеем искать минимум, умеем и максимум: можно домножить веса на −1.
Переберем все (𝐸−𝑉 +1) ребер не из дерева. Если какое-то ребро (𝑣, 𝑢) меньше, чем максимум
на пути 𝑣
𝑢 по дереву, то можно заменить максимум на него и уменьшить вес.
Если такого ребра не нашлось, остов минимален.
Корректность. Представим, что сначала все ребра не из остова имеют бесконечный вес.
Мы по очереди уменьшаем вес каждого. Тогда проверка того, изменился ли остов (умеем с
практики), совпадает с той проверкой, которую мы делаем здесь.
3. Минимум на пути
Способ 1: сжатие путей.
Положим вершины в СНМ со сжатием путей. Вместо ранговой эвристики при join будем
подвешивать нижние вершины к верхним.
dfs. Инвариант: когда мы обошли поддерево 𝑣, ее поддерево в множестве СНМ с корнем 𝑣,
min на пути в СНМ до 𝑣 равен min на пути в дереве до 𝑣.
Выходя из 𝑣, делаем DSU.p[v] = Tree.parent[v].
После обработки поддерева 𝑣 перебираем запросы, для которых она верхняя, делаем get от
нижней, считая min на пути, который прошел get. При переподвешивании вершины меняем
ее вес на минимум по сжатому пути.
Все операции выражены через СНМ со сжатием путей, 𝒪((𝑚 + 𝑛) log 𝑛).
Способ 2: перекрашивание меньшей компоненты.
Сортируем ребра по убыванию, добавляем в таком порядке.
Как только две вершины из запроса попали в одну компоненту, ответ найден.
Храним для каждой вершины номер компоненты.
Пусть сливаем 𝐶1 , 𝐶2 : |𝐶1 | 6 |𝐶2 |. ∀𝑣 ∈ 𝐶1 перебираем все содержащие 𝑣 запросы. Выполняем их, если второй конец лежит в 𝐶2 . Перекрашиваем 𝐶1 в цвет 𝐶2 .
На каждый запрос мы смотрим тогда, когда перекрашиваем один из его концов ⇒ посмотрим
на каждый запрос 6 log 𝑛 раз.
4. Кратчайший путь
Найдем максимальное остовное дерево, ответ – путь в нем.
Чтобы искать путь в дереве, подвесим его, посчитаем глубины всех вершин. Нужен путь
𝑎
𝑏, пусть 𝑑𝑎 > 𝑑𝑏 . Поднимемся из 𝑎 на 𝑑𝑏 − 𝑑𝑎 шагов вверх. Затем будем одноверменно
подниматься из обеих вершин, пока не попадем в одну и ту же.
Корректность. Нашли путь 𝑎
𝑏 с минимальным ребром 𝑚. Если есть путь 𝑝, где минимум
> 𝑚, то там все ребра > 𝑚. Удалим из дерева 𝑚, 𝑎 и 𝑏 оказались в разных компонентах.
Какое-то из ребер 𝑝 соединяет эти компоненты, добавим его в дерево, получили остовное
дерево больше, чем было. Противоречит максимальности дерева.
9/12
Алгоритмы, весна 2019/20
Практика #7. DSU, MST.
5. Random и СНМ
Ясно, что не дольше 𝒪(𝑛).
Ω(𝑛). Коротко: if (rand() & 1) укорачивает пути в два раза по сравнению с реализацией
без него.
Конкретный пример: for i in range(n) join(1, i).
В среднем 𝑛2 раз дерево будет расти на одно ребро вверх.
После этого любой get будет работать за Ω(𝑛).
10/12
Алгоритмы, весна 2019/20
Практика #7. DSU, MST.
4.2. Дополнительная часть
1. Dynamic connectivity in directed graph
Будем пересчитвать 𝑐[𝑎, 𝑏]: число путей из 𝑎 в 𝑏 по модулю 𝑝, где 𝑝 – большое случайное
простое число. Ответ на запрос: connected(𝑎, 𝑏) = (𝑐[𝑎, 𝑏] ̸= 0).
2. Dynamic 2-connectivity
В одной СНМ 𝑑1 будем хранить компоненты связности. Во второй СНМ 𝑑2 будем хранить
компоненты двухсвязности.
Будем также поддерживать дерево компонент двухсвязности: у каждой вершины 𝑣 (компоненты двухсвязности) есть ссылка на отца 𝑝[𝑣] (вершину исхоного графа).
Также будем хранить число мостов. Обработка запроса add(𝑎, 𝑏):
a) 𝑑2 (𝑎) = 𝑑2 (𝑏) ⇒ ничего делать не нужно.
b) 𝑑1 (𝑎) = 𝑑1 (𝑏) ⇒ получаем цикл длины 𝑘, его можно выделить за 𝒪(𝑘), используя только
ссылки на отцов 𝑝[𝑣].
В выделении цикла каждое ребро участвует не более одного раза, в сумме 𝒪(𝑚).
c) 𝑑1 (𝑎) ̸= 𝑑1 (𝑏) ⇒ нужно провести ребро между 𝑎 и 𝑏, то есть или 𝑝[get2 (𝑎)] = 𝑏, или
𝑝[get2 (𝑏)] = 𝑎.
Чтобы ссылки на отца остались корректными, или у 𝑎, или у 𝑏 нужно развернуть путь
до корня.
Развернем более короткий из двух путей. Чтобы определить, какой короче, поднимаемся
параллельно от 𝑎 и 𝑏, пока один из путей не кончится.
Время работы 𝒪(𝐿), где 𝐿 – длина короткого пути.
𝐿 6 размера меньшего дерева ⇒ суммарное время работы 𝒪(𝑛 log 𝑛).
Суммарное время обработки всех запросов 𝒪(𝑚𝛼(𝑛) + 𝑛 log 𝑛).
Решение в offline за 𝒪((𝑚 + 𝑛)𝛼(𝑛)).
Можно заранее для каждого ребра узнать ориентацию в нужную сторону.
Предподсчет: запустим процесс добавления всех ребер. Ребра, которые соединяют компоненты связности, добавим в остовный лес.
В конце этот остовный лес ориентируем – каждое его дерево подвесим за произвольную вершину.
Теперь, когда нужно соединить два дерева, у соединяющего ребра уже есть ориентация, и
соединяет оно обязательно корень одного дерева с какой-то вершиной другого.
3. Одного сжатия путей мало...
Рассмотрим дерево 𝑇0 из одной вершины.
Чтобы из 𝑇𝑖 получить 𝑇𝑖+1 , 2𝑖 раз делаем join корня нашего дерева с новой вершиной (она
становится новым корнем) + get от самой глубокой вершины.
Утверждение: 𝑗-й get работает за Θ(log 𝑗).
Чтобы доказать утверждение, докажем по индукции, что 𝑇𝑖+1 получается, как 𝑇𝑖 + 𝑇𝑖 (подвесили корень одного дерева за корень другого).
Заметим, что если сперва делать серию join, а затем серию get, то время работы всегда
линейное.
Мы построили пример для 𝑚 = Θ(𝑛). Для большего может и не получиться.
11/12
Алгоритмы, весна 2019/20
Практика #7. DSU, MST.
4. Euclidean MST
?
12/12
Скачать