This documentation is automatically generated by online-judge-tools/verification-helper
#include "algorithm/DataStructure/UnionFind/union_find.hpp"
「素集合データ構造 (disjoint-set data structure)」とは,互いに素な動的集合の族を管理する. 簡単にいうと,要素のグループ分けを管理するデータ構造である.
素集合データ構造に対する次の操作のアルゴリズムを「Union-Find」という. 1964年,Bernard A. Galler と Michael J. Fischer が効率的な手法を考案した.
本実装では「素集合森 (disjoint-set forest)」を作成しており,「union by size」と「経路圧縮」による工夫を行っている.
各クエリ処理に要する計算量は,アッカーマン関数の逆関数を $\operatorname{\alpha}(x)$ とすると,$\mathcal{O}(\operatorname{\alpha}(N))$ となる. アッカーマン関数の逆関数は,増加が非常に遅く,$x \leq 10^{80}$ に対し $\operatorname{\alpha}(x) \leq 4$ が成り立つため,実用上 $\mathcal{O}(1)$ とみなすことができる.
#ifndef ALGORITHM_UNION_FIND_HPP
#define ALGORITHM_UNION_FIND_HPP 1
#include <algorithm>
#include <cassert>
#include <utility>
#include <vector>
namespace algorithm {
class UnionFind {
int m_vn; // m_vn:=(要素数).
int m_gn; // m_gn:=(集合の数).
// m_par[x]:=(要素xの親). 0未満の場合,xは代表元であり,値の絶対値は属する集合のサイズを表す.
std::vector<int> m_par;
int root_internal(int x) {
if(m_par[x] < 0) return x;
return m_par[x] = root_internal(m_par[x]); // 経路圧縮.
}
public:
UnionFind() : UnionFind(0) {}
explicit UnionFind(int n) : m_vn(n), m_gn(n), m_par(n, -1) {
assert(n >= 0);
}
// 要素数を取得する.
int vn() const { return m_vn; };
// 集合の数を取得する.
int gn() const { return m_gn; };
// 要素xが属する集合の代表元を取得する.
int root(int x) {
assert(0 <= x and x < vn());
return root_internal(x);
}
// 要素xが属する集合のサイズを取得する.
int size(int x) {
assert(0 <= x and x < vn());
return -m_par[root_internal(x)];
}
// 要素x, yが同じ集合に属するか判定する.
bool is_same(int x, int y) {
assert(0 <= x and x < vn());
assert(0 <= y and y < vn());
return root_internal(x) == root_internal(y);
}
// 要素x, yが属するそれぞれの集合を合併する.
bool unite(int x, int y) {
assert(0 <= x and x < vn());
assert(0 <= y and y < vn());
x = root_internal(x), y = root_internal(y);
if(x == y) return false; // Do nothing.
if(-m_par[x] < -m_par[y]) std::swap(x, y); // Merge technique (union by size).
m_par[x] += m_par[y];
m_par[y] = x;
--m_gn;
return true;
}
void reset() {
m_gn = m_vn;
std::fill(m_par.begin(), m_par.end(), -1);
}
};
} // namespace algorithm
#endif
#line 1 "algorithm/DataStructure/UnionFind/union_find.hpp"
#include <algorithm>
#include <cassert>
#include <utility>
#include <vector>
namespace algorithm {
class UnionFind {
int m_vn; // m_vn:=(要素数).
int m_gn; // m_gn:=(集合の数).
// m_par[x]:=(要素xの親). 0未満の場合,xは代表元であり,値の絶対値は属する集合のサイズを表す.
std::vector<int> m_par;
int root_internal(int x) {
if(m_par[x] < 0) return x;
return m_par[x] = root_internal(m_par[x]); // 経路圧縮.
}
public:
UnionFind() : UnionFind(0) {}
explicit UnionFind(int n) : m_vn(n), m_gn(n), m_par(n, -1) {
assert(n >= 0);
}
// 要素数を取得する.
int vn() const { return m_vn; };
// 集合の数を取得する.
int gn() const { return m_gn; };
// 要素xが属する集合の代表元を取得する.
int root(int x) {
assert(0 <= x and x < vn());
return root_internal(x);
}
// 要素xが属する集合のサイズを取得する.
int size(int x) {
assert(0 <= x and x < vn());
return -m_par[root_internal(x)];
}
// 要素x, yが同じ集合に属するか判定する.
bool is_same(int x, int y) {
assert(0 <= x and x < vn());
assert(0 <= y and y < vn());
return root_internal(x) == root_internal(y);
}
// 要素x, yが属するそれぞれの集合を合併する.
bool unite(int x, int y) {
assert(0 <= x and x < vn());
assert(0 <= y and y < vn());
x = root_internal(x), y = root_internal(y);
if(x == y) return false; // Do nothing.
if(-m_par[x] < -m_par[y]) std::swap(x, y); // Merge technique (union by size).
m_par[x] += m_par[y];
m_par[y] = x;
--m_gn;
return true;
}
void reset() {
m_gn = m_vn;
std::fill(m_par.begin(), m_par.end(), -1);
}
};
} // namespace algorithm