作成日
2016/07/10最終更新
2018/10/15記事区分
一般公開『プログラミングコンテストでのデータ構造』で紹介されている Union-Find 木で集合を表現すると、以下のクエリを高速に実行できます。集合一つが一つの木となるため、複数の集合がある場合は全体として森になります。
- ある要素
a_i
とa_j
が同じ集合に属しているかどうかの判定 - 二つの集合を一つの集合にまとめる
サンプル
#include <iostream>
#include <cstdio>
using namespace std;
const int MAXN = 101024;
// Union Find >>>>>>>>>>>>>
int par[MAXN * 3]; // ノード i の属する Union Find 木におけるノード i の親
int rnk[MAXN * 3]; // ノード i の属する Union Find 木の深さ
void init(int n) { // 森の初期化 (ノード数 1 の木の集合)
for(int i = 0; i < n; ++i) {
par[i] = i; // ノード i のみからなる木
rnk[i] = 0; // 深さ 0
}
}
int find(int x) { // ノード i の属する Union Find 木の root
if(par[x] == x) return x;
else return par[x] = find(par[x]); // root を return しつつ、次回 find の高速化のためにノード x を直接 root につなぐ
}
bool same(int x, int y) { // クエリ1 (判定)
return find(x) == find(y);
}
void unite(int x, int y) { // クエリ2 (二つの木をまとめる)
x = find(x);
y = find(y);
if(x == y) return;
if(rnk[x] < rnk[y]) { // root が x の木の方が root が y の木よりも低い
par[x] = y; // root y に x の木をつなぐ
}
else {
par[y] = x; // root x に y の木をつなぐ
if(rnk[x] == rnk[y]) ++rnk[x]; // 同じ高さだったならばまとめたことによって高さ +1
}
}
// <<<<<<<<<<<<<<< Union Find
int N,K;
int type[MAXN];
int X[MAXN];
int Y[MAXN];
int main() {
cin>>N>>K;
for(int i = 0; i < K; ++i) scanf("%d %d %d", &type[i], &X[i], &Y[i]); // POJ cin 遅い問題対応
for(int i = 0; i < K; ++i) {--X[i]; --Y[i];}
init(N*3); // 初期化
int res = 0;
for(int i = 0; i < K; ++i) {
int x = X[i], y = Y[i];
if(x < 0 || y < 0 || N <= x || N <= y) {
++res;
continue;
}
if(type[i] == 1) {
if(same(x, y + N) || same(x, y + 2*N)) ++res;
else for(int i = 0; i < 3; ++i) unite(x + i*N, y + i*N); // i が A/B/C に属すこと <=同値=> j が A/B/C に属すこと
}
else {
if(same(x, y) || same(x, y + 2*N)) ++res;
else {
unite(x, y + N); // i が A/B/C に属すこと <=同値=> j が B/C/A に属すこと
unite(x + N, y + 2*N);
unite(x + 2*N, y);
}
}
}
cout << res << endl;
return 0;
}
関連記事
- ダウンキャスト (C++をもう一度)実行時型情報 RTTI #include <iostream> #include <typeinfo> using namespace std; class MyClass { public: virtual ~MyClass() {} // typeid で正しい RTTI // (RunTime Type Information; 実行時型情報) ...
- 競技プログラミングの基本処理チートシート (C++)限られた時間の中で問題を解くために必要となる、競技プログラミングにおける基本的な処理のチートシートです。競プロにおけるメジャー言語 C++ を利用します。その際 C++11 の機能は利用せず C++03 の機能の範囲内で記述します。 頻度高く定期的に開催されるコンテスト AtCoder Codeforces main.cpp #include <iostream>
- 構造体と列挙体 (C++をもう一度)構造体 #include <iostream> using namespace std; struct MyStruct { char charval; int intval; }; void Show(MyStruct* obj) { cout << obj->intval << endl; } int main() { ...
- Valgrind による C/C++ メモリリーク検出JVM メモリリークでは JDK の jstat や jmap で原因を調査できます。C/C++ では valgrind の Memcheck ツールが利用できます。valgrind には複数のツールが含まれており既定のツールが Memcheck です。他のツールを利用する場合は --tool オプションで指定します。 [簡単な利用例](h
- クラスの基本/初期化 (C++をもう一度)構造体のように初期化する (非推奨) #include <iostream> using namespace std; const int MAX_STR = 16; class MyClass { public: int m_integer; char m_str[MAX_STR + 1]; void Show(); }; void MyClass::Show...