zoukankan      html  css  js  c++  java
  • 可持久化线段树维护启发式合并的可持久化并查集

    BZOJ 3673: 可持久化并查集

    n个集合 m个操作
    操作:
    1 a b 合并a,b所在集合
    2 k 回到第k次操作之后的状态(查询算作操作)
    3 a b 询问a,b是否属于同一集合,是则输出1否则输出0
    
    0<n,m<=2*10^4
    

    题目链接

    偶然看到这个数据结构,发现挺简单的,前提是会可持久化线段树,可持久化并查集可以说是可持久化线段树的应用。

    并查集维护的是一个父节点指针数组,附带的有时又要维护秩之类的。实习可持久化,我们可以维护多个数组。这样空间肯定是会爆的。那么我们如果直接用主席树维护数组呢。改父亲结点,变成了单点更新。操作回滚变成了普通的主席树回滚。合并,如果不考虑压缩路径直接合并即可。由于主席树中压缩路径不方便回滚(个人感觉)。看了看大牛门的写法,大都是启发式合并,也就是(秩)。到此可持久化线段树可持久化数组可持久化并查集

    看不懂的建议先学个前置技能。并查集的启发式合并,和主席树。

    /**************************************************************
        Problem: 3673
        User: Q1143316492
        Language: C++
        Result: Accepted
        Time:64 ms
        Memory:64576 kb
    ****************************************************************/
     
    /**
     * BZOJ 3673 可持久化线段树 维护启发式合并的可持久化并查集
     */
    //#pragma GCC optimize(2)
    //#pragma comment(linker, "/STACK:1024000000,1024000000")
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #include <vector>
    #include <queue>
    #include <stack>
    #include <cmath>
    #include <cstdlib>
    #include <map>
    #include <set>
     
    using namespace std;
    typedef long long LL;
    const int INF = 0x3F3F3F3F;
    const int INT_MAX = 0x7FFFFFFF;
    const LL LLINF = 0x3F3F3F3F3F3F3F3F;
    const double PI = acos(-1);
    const int MAXN = 2e5 + 10;
    const int MAXM = 1e6 + 10;
     
    struct Node {
        int l, r, faz, rank;
    } tree[MAXN * 20];
     
    int n, m, root[MAXN], tot;
     
    int build(int l, int r) {
        int now = ++tot;
        if(l == r) {
            tree[now].faz = l;
            tree[now].rank = 0;
            return now;
        }
        int mid = (l + r) >> 1;
        tree[now].l = build(l, mid);
        tree[now].r = build(mid + 1, r);
        return now;
    }
     
    int query(int rt, int l, int r, int x) {
        if(l == r) {
            return rt;
        }
        int mid = (l + r) >> 1;
        if(x <= mid) {
            return query(tree[rt].l, l, mid, x);
        } else {
            return query(tree[rt].r, mid + 1, r, x);
        }
    }
     
    int find_root(int rt, int x) {
        int y = query(rt, 1, n, x);
        if(x == tree[y].faz) {
            return y;
        }
        return find_root(rt, tree[y].faz);
    }
     
    int update(int rt, int l, int r, int pos, int val) {
        int now = ++tot;
        tree[now] = tree[rt];
        if(l == r) {
            tree[now].faz = val;
            tree[now].rank = tree[rt].rank;
            return now;
        }
        int mid = (l + r) >> 1;
        if(pos <= mid) {
            tree[now].l = update(tree[now].l, l, mid, pos, val);
        } else {
            tree[now].r = update(tree[now].r, mid + 1, r, pos, val);
        }
        return now;
    }
     
    void add(int now, int l, int r, int pos) {
        if(l == r) {
            tree[now].rank++;
            return ;
        }
        int mid = (l + r) >> 1;
        if(pos <= mid) {
            add(tree[now].l, l, mid, pos);
        } else {
            add(tree[now].r, mid + 1, r, pos);
        }
    }
     
    int main() {
     
        while(~scanf("%d %d", &n, &m)) {
            tot = 0;
            root[0] = build(1, n);
            for(int i = 1; i <= m; i++ ) {
                int opt;
                scanf("%d", &opt);
                if(opt == 1) {
                    int a, b;
                    scanf("%d %d", &a, &b);
                    root[i] = root[i - 1];
                    int x = find_root(root[i], a);
                    int y = find_root(root[i], b);
                    if(tree[x].faz == tree[y].faz) {
                        continue;
                    }
                    if(tree[x].rank > tree[y].rank) {
                        swap(x, y);
                    }
                    root[i] = update(root[i - 1], 1, n, tree[x].faz, tree[y].faz);
                    if(tree[x].rank == tree[y].rank) {
                        add(root[i], 1, n, tree[y].faz);
                    }
                } else if(opt == 2) {
                    int k;
                    scanf("%d", &k);
                    root[i] = root[k];                
                } else if(opt == 3) {
                    int a, b;
                    scanf("%d %d", &a, &b);
                    root[i] = root[i - 1];
                    a = find_root(root[i], a);
                    b = find_root(root[i], b);
                    if(tree[a].faz == tree[b].faz) {
                        puts("1");
                    } else {
                        puts("0");
                    }
                }
            }
        }
     
        return 0;
    }
    
  • 相关阅读:
    (三)Web模块:【1】Web3.0 新特性之使用注解开发
    (三)Web模块:【7】SpringMVC异步请求
    第四章:(1)Web 开发简介
    (三)Web模块:【2】Web3.0 新特性之异步处理
    (三)Web模块:【5】Servlet3.0 与 SpringMVC 整合
    第三章:(2)SpringBoot 日志使用配置
    第三章:(1)日志框架与SLF4j 使用
    (三)Web模块:【6】定制与接管 SpringMVC
    (三)Web模块:【3】Servlet3.0 新特性之 ServletContainerInitializer
    每日日报8月19日
  • 原文地址:https://www.cnblogs.com/Q1143316492/p/9309963.html
Copyright © 2011-2022 走看看