zoukankan      html  css  js  c++  java
  • 可持久化并查集加强版 BZOJ 3674

    http://www.lydsy.com/JudgeOnline/problem.php?id=3674

    3674: 可持久化并查集加强版

    Time Limit: 15 Sec  Memory Limit: 256 MB
    Submit: 3225  Solved: 1192
    [Submit][Status][Discuss]

    Description

    Description:
    自从zkysb出了可持久化并查集后……
    hzwer:乱写能AC,暴力踩标程
    KuribohG:我不路径压缩就过了!
    ndsf:暴力就可以轻松虐!
    zky:……

    n个集合 m个操作
    操作:
    1 a b 合并a,b所在集合
    2 k 回到第k次操作之后的状态(查询算作操作)
    3 a b 询问a,b是否属于同一集合,是则输出1否则输出0
    请注意本题采用强制在线,所给的a,b,k均经过加密,加密方法为x = x xor lastans,lastans的初始值为0
    0<n,m<=2*10^5


    Input

     

    Output

     

    Sample Input

    5 6
    1 1 2
    3 1 2
    2 1
    3 0 3
    2 1
    3 1 2

    Sample Output

    1
    0
    1
     
    题意很简单,就是要你写个可持久化并查集,建议做这题之前先去看我的上一篇博客——可持久化线段树,以及你必须会普通版的并查集。
    如何构造一个可持久化并查集呢?我们知道并查集是由数组实现的,所以只要能写出可持久化数组,自然就能写出可持久化并查集了。查了一些资料,可持久化数组似乎就是可持久化线段树(这个我也不太确定),但是我们还是可以用线段树代替一下数组实现并查集,我们只需要线段树的叶节点去合并就可以了,实际上线段树上面的区间都是没什么用的,但是又不能删掉,因为我们还要维护root[i]。同时要注意并查集合并的时候不要路径压缩,因为压缩后会影响太多点,返回历史版本会很难,所以我们选择人畜无害的按秩合并,同样可以快到飞起。具体实现看代码吧。可持久化并查集可以用pb_ds库的rope,现成模板,我也贴一份模板在下面了,不过这个跑起来的时间是自己写的版本的两倍。(PS:其实是可以进行路径压缩的,但是空间要稍微开大一点)
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    #include <vector>
    #include <queue>
    #include <string>
    #include <stack>
    #include <map>
    #include <set>
    #include <bitset>
    #define X first
    #define Y second
    #define clr(u,v); memset(u,v,sizeof(u));
    #define in() freopen("data","r",stdin);
    #define out() freopen("ans","w",stdout);
    #define Clear(Q); while (!Q.empty()) Q.pop();
    #define pb push_back
    using namespace std;
    typedef long long ll;
    typedef pair<int, int> pii;
    const int maxn = 2e5 + 10;
    const int INF = 0x3f3f3f3f;
    struct Tree
    {
        int l, r, fa;
    } T[maxn<<5];
    int root[maxn], cnt = 0;
    int deep[maxn];//并查集深度
    int n, m;
    
    void build(int l, int r, int &x)//先建树,进行初始化,如果有多case记得cnt初始化为0
    {
        x = ++cnt;
        if (l == r)
        {
            T[x].fa = l;
            deep[l] = 1;
            return ;
        }
        int mid = (l + r) >> 1;
        build(l, mid, T[x].l);
        build(mid + 1, r, T[x].r);
    }
    
    int query(int l, int r, int rt, int pos)//查询pos在线段树中的位置
    {
        if (l == r) return rt;
        int mid = (l + r) >> 1;
        if (mid >= pos) return query(l, mid, T[rt].l, pos);
        else return query(mid + 1, r, T[rt].r, pos);
    }
    
    int find(int x, int cur)//并查集的找祖先
    {
        int pos = query(1, n, root[cur], x);
        if (T[pos].fa != x)
            return find(T[pos].fa, cur);
        return T[pos].fa;
    }
    
    void update(int l, int r, int &x, int y, int pos, int fa)
    {
        T[++cnt] = T[y];//继承上一个版本
        x = cnt;//与父节点连接
        if (l == r)
        {
            T[x].fa = fa;//更新fa值
            return ;
        }
        int mid = (l + r) >> 1;
        if (mid >= pos) update(l, mid, T[x].l, T[y].l, pos, fa);
        else update(mid + 1, r, T[x].r, T[y].r, pos, fa);
    }
    
    void mix(int x, int y, int cur)
    {
        int fx = find(x, cur), fy = find(y, cur);
        if (deep[fx] > deep[fy]) swap(fx, fy);//按秩合并
        if (fx == fy) return ;
        deep[fy] += deep[fx];
        update(1, n, root[cur], root[cur-1], fx, fy);//在线段树fx的位置上更新为fy
    }
    
    int main()
    {
    #ifdef LOCAL
        in();
    #endif
        scanf("%d%d", &n, &m);
        build(1, n, root[0]);
        int cur = 0, lastans = 0;
        while (m--)
        {
            int op, a, b, k;
            scanf("%d", &op);
            if (op == 1)
            {
                scanf("%d%d", &a, &b);
                a ^= lastans;
                b ^= lastans;
                root[cur+1] = root[cur];
                mix(a, b, ++cur);
            }
            else if (op == 2)
            {
                scanf("%d", &k);
                k ^= lastans;
                root[++cur] = root[k];//这里不建议改成cur = k ,因为他可能跳到前面后又跳回去,这样会覆盖掉历史版本
            }
            else
            {
                scanf("%d%d", &a, &b);
                a ^= lastans;
                b ^= lastans;
                root[cur+1] = root[cur];
                ++cur;
                lastans = (find(a, cur) == find(b, cur));
                printf("%d
    ", lastans);
            }
        }
        return 0;
    }

    pb_ds库的rope:

    #include <bits/stdc++.h>
    #include <cstdio>
    #include <ext/rope>
    using namespace std;
    using namespace __gnu_cxx;
    const int maxn = 2e5 + 10;
    rope <int> *f[maxn];
    rope <int> :: iterator it;
    int id[maxn];
    int n, m, op, a, b;
    int find (int &t, int x)
    {
        if (f[t]->at(x) == x) return x;
        int fa = find(t, f[t]->at(x));
        if (fa == f[t]->at(x)) return fa;
        f[t]->replace(x, fa);
        return fa;
    }
    void mix(int &t, int x, int y)
    {
        int fx = find(t, x), fy = find(t, y);
        f[t]->replace(fy, fx);
    }
    int lastans = 0;
    int main()
    {
        scanf("%d%d", &n, &m);
        for (int i = 0; i <= n; ++i) id[i] = i;
        f[0] = new rope<int>(id, id + n + 1);
        for (int i = 1; i <= m; ++i)
        {
            f[i] = new rope <int>(*f[i-1]);
            scanf("%d", &op);
            if (op == 1)
            {
                scanf("%d%d", &a, &b);
                mix(i, a ^ lastans, b ^ lastans);
            }
            else if (op == 2)
            {
                scanf("%d", &a);
                f[i] = f[lastans^a];
            }
            else if (op == 3)
            {
                scanf("%d%d", &a, &b);
                printf("%d
    ", lastans = find(i, a ^ lastans) == find(i, b ^ lastans));
            }
        }
        return 0;
    }

     附上路径压缩版本,路径压缩要updata的次数更多,所以空间也会大点,实测和按秩合并速度差不多,大概是updata也花了时间

    int find(int x, int cur)//并查集的找祖先
    {
        int pos = query(1, n, root[cur], x);
        if (T[pos].fa != x)
        {
            int temp = find(T[pos].fa, cur);
            update(1, n, root[cur], root[cur], T[pos].fa, temp);
            return temp;
        }
        return T[pos].fa;
    }
    
    

      

  • 相关阅读:
    Ubuntu Java环境变量配置
    Ubuntu 获得超级用户权限
    ubuntu 修改主机名
    NGSQC toolkit
    MySQL 常用命令
    Yii的事件和行为的区别和应用
    YII使用PHPExcel导入Excel文件的方法
    Yii: 扩展CGridView增加导出CSV功能
    YII中使用SOAP一定要注意的一些东西
    Yii 多表关联relations
  • 原文地址:https://www.cnblogs.com/scaugsh/p/6863853.html
Copyright © 2011-2022 走看看