[BZOJ]3674 可持久化并查集

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

Time Limit: 15 Sec Memory Limit: 256 MB
Submit: 4051 Solved: 1503
[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

题解

可持久化并查集… 其实我们发现每次合并如果按秩合并的话只会改变一个点的fa, 那么就好比主席树每次有一个点不同, 修改一条链. 那么我们就可以用主席树维护每个点的fa和每个点的rk(即按秩合并的rk). 按秩合并本身暴力爬树是log, 每次查fa也变成log, 那么就是log^2n的复杂度… 用主席树维护即可.
用路径压缩有两点不对:
1. 第i棵主席树的建立由于路径压缩会改变多个点的fa, 那么空间会增加很大一部分, 不像按秩合并只会改变一个点的fa.
2. 多次合并, 每次合并一个集合的root和一个单点, 触发find后等于无效. 这样多次操作就成了一条长链… 成一条长链后那么每次操作回到这个版本, 那么路径压缩就会很长.

#include<stdio.h>
#include<algorithm>
#define Boc register char
#define Acce register int
const int maxn = 2e5 + 5;
int n, m, ans;
inline const int read()
{
    Acce x = 0;
    Boc ch = getchar();
    while (ch < '0' || ch > '9') ch = getchar();
    while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
    return x;
}
struct node
{
    int fa, rk;
    node *ls, *rs;
}pool[maxn * 50], *root[maxn], *tail = pool;
node* build(int lf, int rg)
{
    node* bt = ++ tail;
    if (lf == rg)
    {
        bt -> fa = lf, bt -> rk = 0;
        return bt;
    }
    int mid = (lf + rg) >> 1;   
    bt -> ls = build(lf, mid);
    bt -> rs = build(mid + 1, rg);
    return bt;
}
void modify(node* &bt, node* pre, int lf, int rg, int pos, int val)
{
    bt = ++ tail;
    if(lf == rg) { bt -> fa  = val; return; }
    int mid = (lf + rg) >> 1;
    if(pos <= mid) bt -> rs = pre -> rs, modify(bt -> ls, pre -> ls, lf, mid, pos, val);
    else bt -> ls = pre -> ls, modify(bt -> rs, pre -> rs, mid + 1, rg, pos, val);
}
node* query(node* bt, int lf, int rg, int pos)
{
    if(lf == rg) return bt;
    int mid = (lf + rg) >> 1;
    if(pos <= mid) return query(bt -> ls, lf, mid, pos);
    else return query(bt -> rs, mid + 1, rg, pos);
}
node* find(int u, int i)
{
    node* p = query(root[i], 1, n, u);
    if(u == p -> fa) return p;
    return find(p -> fa, i);
}
int main()
{
    n = read(), m = read();
    root[0] = build(1, n);
    node *p, *q;
    for (Acce i = 1; i <= m; ++ i)
    {
        int opt = read(), u, v;
        if (opt == 1)
        {
            u = read(), v = read();
            root[i] = root[i - 1];
            u ^= ans, v ^= ans;
            p = find(u, i - 1), q = find(v, i - 1);
            if (p -> fa == q -> fa) continue;
            if (p -> rk > q -> rk) std :: swap(p, q);
            modify(root[i], root[i - 1], 1, n, p -> fa, q -> fa); 
            if(p -> rk == q -> rk) q -> rk ++;
        }
        if (opt == 2) u = read(), u ^= ans, root[i] = root[u];
        if (opt == 3)
        {
            root[i] = root[i - 1];
            u = read(), v = read();
            u ^= ans, v ^= ans;
            p = find(u, i), q = find(v, i);
            ans = (p -> fa == q -> fa);
            printf("%d\n", ans);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值