【模板】【P3402】可持久化并查集

(题面来自洛谷)

题目描述
n个集合 m个操作

操作:
1 a b 合并a,b所在集合

2 k 回到第k次操作之后的状态(查询算作操作)

3 a b 询问a,b是否属于同一集合,是则输出1否则输出0

\(n \le 10^5, m \le 2\times 10^5\)

考虑不带路径压缩、使用启发式合并的并查集,每一次合并实际上只是改变了两个点的信息。
1. v的父亲置为u
2. \(size(u) += size(v)\)

那么将数组fa、size改为可持久化数组维护即可。
复杂度分析:根据启发式合并性质,每次Find操作会执行\(logn\)次循环,循环中为可持久化数组查询,故Find操作的单次复杂度为\(O(log^2n)\)

代码:

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
typedef long long LL;
const int maxn(200010);
int n, m;
struct Seg_tree {
    #define mid ((l + r) >> 1)
    #define lc(nd) seg[nd].lc
    #define rc(nd) seg[nd].rc
    
    struct node {
        int dat, lc, rc;
    /*    node(int a = 0, int b = 0, int c = 0):
        	dat(a), lc(b), rc(c) {}*/
    //    node(): dat(0), lc(0), rc(0) {}
    } seg[maxn * 40];
    int root[maxn], tot;
    void modify(int& nd, int pre, int l, int r, int pos, int x) {
        nd = ++tot;
        seg[nd] = seg[pre];
        if (l == r) {
            seg[nd] = (node) {x, 0, 0};
            return;
        }
        if (pos <= mid) modify(lc(nd), lc(pre), l, mid, pos, x);
        else modify(rc(nd), rc(pre), mid+1, r, pos, x);
    }
    void build(int &nd, int l, int r, int val) {
    	nd = ++tot;
    	if (l == r) {
    		seg[nd] = (node) {val, 0, 0};
    		return;
    	}
    	build(lc(nd), l, mid, val);
    	build(rc(nd), mid+1, r, val);
    	return;
    }
    int query(int nd, int l, int r, int pos) {
        if (!nd) return 0;
        if (l == r) return seg[nd].dat;
        if (pos <= mid) return query(lc(nd), l, mid, pos);
        return query(rc(nd), mid+1, r, pos);
    }
} Dsu, Siz;
int Find(int x, int ver) {
    int tmp;
    while (tmp = Dsu.query(Dsu.root[ver], 1, n, x)) x = tmp;
    return x;
}
inline void merge(int u, int v, int ver) {
    u = Find(u, ver), v = Find(v, ver);
    if (u == v) return;
    int a, b;
    if ((a = Siz.query(Siz.root[ver], 1, n, u)) < (b = Siz.query(Siz.root[ver], 1, n, v))) swap(u, v);
    Dsu.modify(Dsu.root[ver], Dsu.root[ver-1], 1, n, v, u);
    Siz.modify(Siz.root[ver], Siz.root[ver-1], 1, n, u, a + b);
    return;
}
int main() {
//	freopen("test.in", "r", stdin);
//	freopen("test.ans", "w", stdout);
    scanf("%d %d", &n, &m);
    Siz.build(Siz.root[0], 1, n, 1);
    int op, u, v;
    for (int i = 1; i <= m; ++i) {
        scanf("%d %d", &op, &u);
        if (op == 1) {
        	Siz.root[i] = Siz.root[i-1];
			Dsu.root[i] = Dsu.root[i-1];
            scanf("%d", &v);
            merge(u, v, i);
        } else if (op == 2) {
            Siz.root[i] = Siz.root[u];
			Dsu.root[i] = Dsu.root[u];
        } else {
        	Siz.root[i] = Siz.root[i-1];
			Dsu.root[i] = Dsu.root[i-1];
            scanf("%d", &v);
            putchar(Find(u, i) == Find(v, i) ? '1' : '0');
            putchar('\n');
        }
    }
    return 0;
}
posted @ 2020-02-06 10:16  onyYuan  阅读(224)  评论(0编辑  收藏  举报