zoukankan      html  css  js  c++  java
  • 【数据结构】可持久化并查集

    事实上是并查集套一个可持久化数组。

    int a[100005];
    
    struct SegmentTree {
    
    #define ls lch[o]
    #define rs rch[o]
    
        static const int MAXN = 1e5 + 10;
        static const int MAXM = 2e5 + 10;
    
        int n;
    
        int top;
        int lch[MAXN * 2 + MAXM * 18];
        int rch[MAXN * 2 + MAXM * 18];
        int sum[MAXN * 2 + MAXM * 18];
    
        int ver[MAXM], cur;
    
        int Clone(int o) {
            ++top;
            sum[top] = sum[o];
            lch[top] = ls;
            rch[top] = rs;
            return top;
        }
    
        void PushUp(int o) {
            sum[o] = sum[ls] + sum[rs];
        }
    
        int BuildHelp(int l, int r) {
            int o = Clone(0);
            if(l == r) {
                sum[o] = a[l];
                ls = 0;
                rs = 0;
                return o;
            }
            int m = (l + r) >> 1;
            ls = BuildHelp(l, m);
            rs = BuildHelp(m + 1, r);
            PushUp(o);
            return o;
        }
    
        int AddHelp(int o, int l, int r, int p, ll v) {
            o = Clone(o);
            if(l == r) {
                sum[o] += v;
                return o;
            }
            int m = (l + r) >> 1;
            if(p <= m)
                ls = AddHelp(ls, l, m, p, v);
            if(p >= m + 1)
                rs = AddHelp(rs, m + 1, r, p, v);
            PushUp(o);
            return o;
        }
    
        int SetHelp(int o, int l, int r, int p, ll v) {
            o = Clone(o);
            if(l == r) {
                sum[o] = v;
                return o;
            }
            int m = (l + r) >> 1;
            if(p <= m)
                ls = SetHelp(ls, l, m, p, v);
            if(p >= m + 1)
                rs = SetHelp(rs, m + 1, r, p, v);
            PushUp(o);
            return o;
        }
    
        int SumHelp(int o, int l, int r, int ql, int qr) {
            if(ql <= l && r <= qr)
                return sum[o];
            int m = (l + r) >> 1;
            int res = 0;
            if(ql <= m)
                res = res + SumHelp(ls, l, m, ql, qr);
            if(qr >= m + 1)
                res = res + SumHelp(rs, m + 1, r, ql, qr);
            return res;
        }
    
        void Build(int _n) {
            n = _n;
            cur = 0;
            top = 0;
            ver[++cur] = BuildHelp(1, n);
        }
    
        void Add(int id, int pos, int val) {
            ver[++cur] = AddHelp(ver[id], 1, n, pos, val);
        }
    
        void Set(int id, int pos, int val) {
            ver[++cur] = SetHelp(ver[id], 1, n, pos, val);
        }
    
        int Sum(int id, int lpos, int rpos) {
            return SumHelp(ver[id], 1, n, lpos, rpos);
        }
    
    };
    
    struct DisjointSet {
    
        int n;
    
        SegmentTree find;
        SegmentTree size;
    
        int ver[200005], cur;
    
        void Init(int n) {
            cur = 0;
            for(int i = 1; i <= n; i++)
                a[i] = i;
            find.Build(n);
            for(int i = 1; i <= n; i++)
                a[i] = 1;
            size.Build(n);
            ver[++cur] = find.cur;
        }
    
        int Find(int id, int x) {
            int fx = find.Sum(ver[id], x, x);
            if(fx == x)
                return x;
            return Find(id, fx);
        }
    
        int Size(int id, int x) {
            x = Find(id, x);
            return size.Sum(id, x, x);
        }
    
        int Merge(int id, int x, int y) {
            x = Find(id, x);
            y = Find(id, y);
            if(x == y) {
                ver[++cur] = ver[id];
                return 0;
            }
            int sx = size.Sum(ver[id], x, x);
            int sy = size.Sum(ver[id], y, y);
            if(sx < sy) {
                swap(x, y);
                swap(sx, sy);
            }
            find.Set(ver[id], y, x);
            size.Add(ver[id], x, sy);
            ver[++cur] = find.cur;
            return x;
        }
    
    } ds;
    

    这里默认merge操作产生一个新的版本。并查集的ver[u]表示并查集的版本u对应线段树的版本ver[u]

    也就是说,并查集的ver[u]对应的线段树根为find.ver[ver[u]],这里find和size的树根始终处于同一版本号,同步变化。
    注意分清楚哪些操作产生新的版本。

    假如是验证中的例题,跳转操作也产生新的版本,其他操作都在并查集的最后一个版本上修改(和可持久化数组的每个修改都带old版本不同)。

    注意并查集的最后一个版本不见得对应线段树的最后一个版本,因为这里为了节省空间使得线段树并不会因为Sum操作产生版本。

  • 相关阅读:
    为什么叫做重叠端口?
    为什么叫做重叠端口?
    拷贝构造函数的参数为什么必须使用引用类型
    拷贝构造函数的参数为什么必须使用引用类型
    2018 网易校招题目
    似友非友?
    贫穷与富有
    (OK) Android
    Android
    (OK) Android
  • 原文地址:https://www.cnblogs.com/purinliang/p/14285206.html
Copyright © 2011-2022 走看看