洛谷P3402 可持久化並查集 題解
阿新 • • 發佈:2021-11-08
題目連結:https://www.luogu.com.cn/problem/P3402
題目大意:
給定 \(n\) 個集合,第 \(i\) 個集合內初始狀態下只有一個數,為 \(i\)。
有 \(m\) 次操作。操作分為 \(3\) 種:
-
1 a b
合併 \(a,b\) 所在集合; -
2 k
回到第 \(k\) 次操作(執行三種操作中的任意一種都記為一次操作)之後的狀態; -
3 a b
詢問 \(a,b\) 是否屬於同一集合,如果是則輸出 \(1\) ,否則輸出 \(0\)。
解題思路:
可持久化並查集。並查集 按秩合併,不要路徑壓縮。
示例程式:
#include <bits/stdc++.h> using namespace std; const int maxn = 100010; struct Tree { int l, r,val; } tree[maxn*80]; int tot, rootfa[maxn*2], rootdep[maxn*2]; int n, m; void build(int l, int r, int& rt) { rt = ++tot; if (l == r) { tree[rt].val = l; return; } int mid = (l + r) / 2; build(l, mid, tree[rt].l); build(mid+1, r, tree[rt].r); } void update(int p, int val, int l, int r, int pt, int &rt) { tree[rt = ++tot] = tree[pt]; if (l == r) { tree[rt].val = val; return; } int mid = (l + r) / 2; if (p <= mid) update(p, val, l, mid, tree[pt].l, tree[rt].l); else update(p, val, mid+1, r, tree[pt].r, tree[rt].r); } int query(int p, int l, int r, int rt) { if (l == r) return tree[rt].val; int mid = (l + r) / 2; if (p <= mid) return query(p, l, mid, tree[rt].l); else return query(p, mid+1, r, tree[rt].r); } int ffind(int ver, int x) { int fa = query(x, 1, n, rootfa[ver]); return fa == x ? x : ffind(ver, fa); } void funion(int ver, int x, int y) { x = ffind(ver-1, x); y = ffind(ver-1, y); if (x == y) { rootfa[ver] = rootfa[ver-1]; rootdep[ver] = rootdep[ver-1]; } else { int depx = query(x, 1, n, rootdep[ver-1]); int depy = query(y, 1, n, rootdep[ver-1]); if (depx > depy) swap(x, y); update(x, y, 1, n, rootfa[ver-1], rootfa[ver]); if (depx == depy) { update(y, depy+1, 1, n, rootdep[ver-1], rootdep[ver]); } else { rootdep[ver] = rootdep[ver-1]; } } } int main() { ios::sync_with_stdio(0); cin >> n >> m; build(1, n, rootfa[0]); for (int i = 1; i <= m; i ++) { int op, k, a, b; cin >> op; if (op == 2) cin >> k; else cin >> a >> b; if (op == 1) { // 合併a,b所在集合 funion(i, a, b); } else if (op == 2) { // 回到第k次操作 rootfa[i] = rootfa[k]; rootdep[i] = rootdep[k]; } else { // 詢問a,b是否屬於同一集合 rootfa[i] = rootfa[i-1]; rootdep[i] = rootdep[i-1]; cout << (ffind(i, a) == ffind(i, b) ? 1 : 0) << endl; } } return 0; }