← Все вопросы

Как через DSU посчитать число компонент связности после каждого добавленного ребра?

Задан 9 месяцев назад195 просмотров2 ответа
7

n вершин, изначально 0 рёбер. Рёбра приходят по одному, после каждого надо вывести текущее число компонент связности. Хочется онлайн и быстро. Как держать счётчик корректно, учитывая кратные рёбра (которые не меняют связность)?

2 ответа

11
✓ Принятый ответ — помог автору

Старт: comps = n (каждая вершина — своя компонента). Каждое успешное объединение (концы были в разных компонентах) уменьшает счётчик на 1; кратное ребро внутри одной компоненты ничего не меняет.

DSU dsu(n);          // comps инициализирован как n внутри DSU
for (auto& [u, v] : edges) {
    dsu.unite(u, v); // внутри: если слил разные — --comps
    cout << dsu.comps << '\n'; // число компонент после этого ребра
}

Где unite ровно такой, как в DSU с размерами: возвращает false и не трогает comps, если find(u)==find(v) (кратное ребро/петля). Это автоматически корректно обрабатывает кратные рёбра — они просто не уменьшают счётчик.

Сложность — O(α(n)) на ребро, итого O((n+m)·α(n)), память O(n). Онлайн, ничего предсчитывать не нужно. Если попросят ещё и размер наибольшей компоненты после каждого ребра — держите maxSize и обновляйте его в unite после слияния (maxSize = max(maxSize, sz[корня])).

4

Обратная задача (рёбра удаляются, число компонент растёт) онлайн так не решается — DSU не умеет разъединять. Её сводят к этой через обращение времени (обрабатываем удаления как добавления с конца, см. отдельный вопрос про реверс времени). И аккуратно с самопетлями u==v: они не должны уменьшать comps; в unite это уже покрыто проверкой find(u)==find(v), но если вы пишете счётчик вручную снаружи, легко забыть и получить заниженное число компонент.

Ваш ответ

Войдите, чтобы ответить на вопрос.