• 【模板】【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 imes 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('
    ');
            }
        }
        return 0;
    }
    
  • 相关阅读:
    练习1-17 编写一个程序,打印长度大于80个字符的所有输入行.
    练习1-16 修改打印最长文本行的程序的主程序main, 使之可以打印任意长度的输入行的长度, 并尽可能多的打印文本。
    惠普Z620工作站用安装版装win7旗舰版64位结果找不到硬盘
    输入元素( Input Element)
    GradientStop
    qml关键字style
    opacity
    QT5-step-by-step-LayoutManagement
    QT5-step-by-step-BasicKnowledge
    Adeneo Embedded: Building Qt 5.1 for Freescale i.MX6Introduction on LTIB
  • 原文地址:https://www.cnblogs.com/TY02/p/12267758.html
Copyright © 2020-2023  润新知