H - 連結成分 / Connected Components Editorial by MMNMM


union find とともに https://noshi91.hatenablog.com/entry/2019/07/19/180606 で述べられているデータ構造を管理することでこの問題を解きます。

次の \(3\) つの配列を持ち、更新を行います。

\[\begin{aligned} \operatorname{parent}[v]&\coloneqq v \text{ の union find 木における親}\\ \operatorname{size}[v]&\coloneqq v \text{ が union find 木の根であるとき、その木の頂点数}\\ \operatorname{next}[v]&\coloneqq v \text{ の次に列挙される頂点} \end{aligned}\]

はじめ、どの \(v\) についても \(\operatorname{parent}[v]=\operatorname{next}[v]=v,\operatorname{size}[v]=1\) です。

\(\operatorname{parent}\) と \(\operatorname{size}\) については通常の union find と同様にして更新を行います。 \(u,v\) に辺を張るクエリが来たとき、union find でも併合が行われたなら(つまり、クエリの直前で \(u,v\) が属する連結成分が異なっていたなら)、\(\operatorname{next}[u]\) と \(\operatorname{next}[v]\) を入れ替えます。

連結成分内の頂点を列挙するクエリが来たとき、無限列 \(v,\operatorname{next}[v],\operatorname{next}[\operatorname{next}[v]],\ldots\) が( \(2\) 項目以降で)はじめて \(v\) と等しくなるまで列挙し、ソートして出力すればよいです。

時間計算量は、出力すべき合計頂点数を \(V_{\operatorname{sum}}\) として \(O(N+V_{\operatorname{sum}}\log N+Q\alpha(Q,N))\) となります。

実装は以下のようになります。 実際の提出

#include <vector>
#include <numeric>
#include <algorithm>

class enumerable_union_find {
    std::vector<unsigned long> next_vertex, uf_parent, uf_size;

    unsigned long find(unsigned long i) {
        while (i != uf_parent[i])i = uf_parent[i] = uf_parent[uf_parent[i]];
        return i;
    }

public:
    explicit enumerable_union_find(const unsigned long N) : next_vertex(N), uf_parent(N), uf_size(N, 1) {
        std::iota(begin(next_vertex), end(next_vertex), 0UL);
        std::iota(begin(uf_parent), end(uf_parent), 0UL);
    }

    void unite(unsigned long i, unsigned long j) {
        i = find(i);
        j = find(j);
        if (i == j)return;
        if (uf_size[i] < uf_size[j])std::swap(i, j);
        std::swap(next_vertex[i], next_vertex[j]);
        uf_size[i] += uf_size[j];
        uf_parent[j] = i;
    }

    std::vector<unsigned long> enumerate(unsigned long i) {
        std::vector<unsigned long> ret{i};
        auto now{i};
        while (i != next_vertex[now])ret.emplace_back(now = next_vertex[now]);
        std::sort(begin(ret), end(ret));
        return ret;
    }
};

#include <iostream>
using namespace std;

int main() {
    int N, Q;
    cin >> N >> Q;

    enumerable_union_find uf(N);

    int t, u, v;
    for (int i = 0; i < Q; ++i) {
        cin >> t;
        if (t == 1) {
            cin >> u >> v;
            uf.unite(u - 1, v - 1);
        } else {
            cin >> u;
            for (const auto x : uf.enumerate(u - 1))cout << x + 1 << " ";
            cout << endl;
        }
    }

    return 0;
}

posted:
last update: