This documentation is automatically generated by online-judge-tools/verification-helper
#define PROBLEM "https://judge.yosupo.jp/problem/persistent_unionfind"
#include <iostream>
#include <tuple>
#include <vector>
#include "../algorithm/DataStructure/UnionFind/rollback_union_find.hpp"
int main() {
int n;
int q;
std::cin >> n >> q;
// queries[k]:=(k番目のグラフに対するクエリ). tuple of (type, u, v, index).
std::vector<std::vector<std::tuple<int, int, int, int>>> queries(q + 1);
for(int i = 1; i <= q; ++i) {
int t;
int k;
int u, v;
std::cin >> t >> k >> u >> v;
k++;
queries[k].emplace_back(t, u, v, i);
}
std::vector<int> ans(q + 1, -1);
algorithm::RollbackUnionFind uf(n);
auto dfs = [&](auto self, int t, int u, int v, int i) -> void {
if(t == 0) {
uf.unite(u, v);
for(const auto &[nt, nu, nv, ni] : queries[i]) self(self, nt, nu, nv, ni);
uf.rollback();
} else {
ans[i] = uf.is_same(u, v);
}
};
for(const auto &[t, u, v, i] : queries[0]) dfs(dfs, t, u, v, i);
for(auto elem : ans) {
if(elem != -1) std::cout << elem << "\n";
}
}
#line 1 "verify/yosupo-persistent_unionfind-rollback_union_find.test.cpp"
#define PROBLEM "https://judge.yosupo.jp/problem/persistent_unionfind"
#include <iostream>
#include <tuple>
#include <vector>
#line 1 "algorithm/DataStructure/UnionFind/rollback_union_find.hpp"
#include <algorithm>
#include <cassert>
#include <stack>
#line 8 "algorithm/DataStructure/UnionFind/rollback_union_find.hpp"
#include <utility>
#line 10 "algorithm/DataStructure/UnionFind/rollback_union_find.hpp"
namespace algorithm {
// Rollback付きUnion-Find.
class RollbackUnionFind {
int m_vn; // m_vn:=(要素数).
int m_gn; // m_gn:=(集合の数).
// m_par[x]:=(要素xの親). 0未満の場合,xは代表元であり,値の絶対値は属する集合のサイズを表す.
std::vector<int> m_par;
std::stack<std::tuple<int, int, int, int>> m_history; // tuple of (x, m_par[x], y, m_par[y]).
int root_internal(int x) const {
if(m_par[x] < 0) return x;
return root_internal(m_par[x]);
}
public:
RollbackUnionFind() : RollbackUnionFind(0) {}
explicit RollbackUnionFind(int n) : m_vn(n), m_gn(n), m_par(n, -1) {
assert(n >= 0);
}
// 現在のヒストリーのインデックスを取得する.O(1).
int index() const { return m_history.size(); }
// 要素数を返す.
int vn() const { return m_vn; };
// 集合の数を返す.
int gn() const { return m_gn; };
// 要素xが属する集合の代表元を取得する.O(log N).
int root(int x) const {
assert(0 <= x and x < vn());
return root_internal(x);
}
// 要素xが属する集合のサイズを取得する.O(log N).
int size(int x) const { return -m_par[root(x)]; }
// 要素x, yが同じグループに属するか判定する.O(log N).
bool is_same(int x, int y) const { return root(x) == root(y); }
// 要素x, yが属するそれぞれの集合を合併する.O(log N).
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);
m_history.emplace(x, m_par[x], y, m_par[y]);
if(x == y) return false;
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;
}
// 直前の状態に戻す.O(1).
void rollback() {
assert(!m_history.empty());
auto [x, xp, y, yp] = m_history.top();
m_history.pop();
m_par[x] = xp, m_par[y] = yp;
}
void reset() {
m_gn = m_vn;
std::fill(m_par.begin(), m_par.end(), -1);
std::stack<std::tuple<int, int, int, int>>().swap(m_history);
}
};
} // namespace algorithm
#line 8 "verify/yosupo-persistent_unionfind-rollback_union_find.test.cpp"
int main() {
int n;
int q;
std::cin >> n >> q;
// queries[k]:=(k番目のグラフに対するクエリ). tuple of (type, u, v, index).
std::vector<std::vector<std::tuple<int, int, int, int>>> queries(q + 1);
for(int i = 1; i <= q; ++i) {
int t;
int k;
int u, v;
std::cin >> t >> k >> u >> v;
k++;
queries[k].emplace_back(t, u, v, i);
}
std::vector<int> ans(q + 1, -1);
algorithm::RollbackUnionFind uf(n);
auto dfs = [&](auto self, int t, int u, int v, int i) -> void {
if(t == 0) {
uf.unite(u, v);
for(const auto &[nt, nu, nv, ni] : queries[i]) self(self, nt, nu, nv, ni);
uf.rollback();
} else {
ans[i] = uf.is_same(u, v);
}
};
for(const auto &[t, u, v, i] : queries[0]) dfs(dfs, t, u, v, i);
for(auto elem : ans) {
if(elem != -1) std::cout << elem << "\n";
}
}