公式
H - 連結成分 / Connected Components 解説
by
H - 連結成分 / Connected Components 解説
by
KoD
この問題はマージテク (weighted union rule) と呼ばれるテクニックを用いて解くことができます。
連結成分ごとに、その成分に属する頂点を配列などのデータ構造で管理することにします。連結成分 \(A, B\) をマージするとき、\(|A| \leq |B|\) なら \(A\) に属する頂点を \(B\) に対応するデータ構造に push し、\(|A| \gt |B|\) なら \(B\) に属する頂点を \(A\) に対応するデータ構造に push します。
各頂点について、何回別のデータ構造に移されるか考えてみましょう。\(|A| + |B| \geq 2 \min(|A|, |B|)\) から、ある頂点が別のデータ構造に移されるとき、その頂点が属する連結成分の大きさは \(2\) 倍以上になることがわかります。連結成分の大きさは最大でも \(N\) 以下なので、移された回数を \(k\) とおくと \(2^k \leq N\) すなわち \(k \leq \log_{2}{N}\) が成り立ちます。
全ての頂点について上記の値を合計した値は \(N \log_{2}{N}\) であるので、この問題を \(O(N \log N)\) で解くことができました。
実装例 (C++) :
#include <bits/stdc++.h>
using namespace std;
int main() {
int n, q;
cin >> n >> q;
vector<int> par(n);
vector<vector<int>> list(n);
for (int i = 0; i < n; ++i) {
par[i] = i;
list[par[i]].push_back(i);
}
while (q--) {
int t;
cin >> t;
if (t == 1) {
int u, v;
cin >> u >> v;
u -= 1, v -= 1;
if (par[u] != par[v]) {
if (list[par[u]].size() < list[par[v]].size()) {
swap(u, v);
}
const int a = par[u], b = par[v];
for (const int x : list[b]) {
list[a].push_back(x);
par[x] = a;
}
list[b].clear();
}
} else {
int u;
cin >> u;
u -= 1;
auto& vec = list[par[u]];
sort(begin(vec), end(vec));
const int m = vec.size();
for (int i = 0; i < m; ++i) {
cout << vec[i] + 1 << " \n"[i + 1 == m];
}
}
}
}
投稿日時:
最終更新:
