BZOJ-3674 可持久化并查集加强版 (主席树+并查集按秩合并)

题目链接
题解:
主席树维护并查集的 father 数组以及它的历史状态。
我觉得比较难搞得是按秩合并,我看网上其他人的代码觉得合并的好奇怪,然后我按照自己的想法合并觉得更奇怪,但是最奇怪的是它竟然过了!我怀疑是不是数据水了,然后去 洛谷acwing 交了一下,竟然都过了!
这里由于按秩合并优化是不影响答案的正确性的,所以我合并部分的正确与否先保留。(因为我合并时会将以前的版本中的节点的秩改变)
代码:

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef unsigned long long ull;
#define PI acos(-1.0)
#define INF 0x3f3f3f3f3f3f3f3f
#define P pair<ll, int>
#define debug(x) cout << (#x) << ": " << (x) << " "
#define fastio ios::sync_with_stdio(false), cin.tie(0)
const int mod = 1e9 + 7;
const int M = 1000000 + 10;
const int N = 200000 + 10;

int n, m;
struct node { int ls, rs, f, dep; } tree[N<<5];
int rt[N], cnt;

void build(int &now, int l = 1, int r = n)
{
    now = ++cnt;
    if(l == r) {
        tree[now].f = l;
        tree[now].dep = 1;
        return ;
    }
    int mid = (l + r) >> 1;
    build(tree[now].ls, l, mid);
    build(tree[now].rs, mid + 1, r);
}

int query_id(int now, int pos, int l = 1, int r = n)
{
    if(l == r) return now;
    int mid = (l + r) >> 1;
    if(pos <= mid) return query_id(tree[now].ls, pos, l, mid);
    return query_id(tree[now].rs, pos, mid + 1, r);
}

int finds(int i, int x)
{
    int id = query_id(rt[i], x);
    if(tree[id].f == x) return id;
    return finds(i, tree[id].f);
}

void update(int &now, int pre, int a, int b, int l = 1, int r = n)
{
    now = ++cnt;
    if(l == r) {
        tree[now].f = b;
        tree[now].dep = tree[pre].dep;
        return ;
    }
    tree[now].ls = tree[pre].ls, tree[now].rs = tree[pre].rs;
    int mid = (l + r) >> 1;
    if(a <= mid) update(tree[now].ls, tree[pre].ls, a, b, l, mid);
    else update(tree[now].rs, tree[pre].rs, a, b, mid + 1, r);
}

void update_dep(int now, int b, int a_dep, int l = 1, int r = n)
{
    if(l == r) {
        tree[now].dep = max(a_dep + 1, tree[now].dep);
        return ;
    }
    int mid = (l + r) >> 1;
    if(b <= mid) update_dep(tree[now].ls, b, a_dep, l, mid);
    else update_dep(tree[now].rs, b, a_dep, mid + 1, r);
}

void unions(int i, int a, int b)
{
    int id_ra = finds(i - 1, a), id_rb = finds(i - 1, b);
    if(tree[id_ra].dep > tree[id_rb].dep) swap(id_ra, id_rb);
    update(rt[i], rt[i - 1], tree[id_ra].f, tree[id_rb].f);
    update_dep(rt[i], tree[id_rb].f, tree[id_ra].dep);
}

signed main()
{
    scanf("%d %d", &n, &m);
    build(rt[0]);
    int op, a, b, k;
    for(int i = 1; i <= m; i ++) {
        scanf("%d", &op);
        if(op == 1) {
            scanf("%d %d", &a, &b);
            unions(i, a, b);
        } else if(op == 2) {
            scanf("%d", &k);
            rt[i] = rt[k];
        } else {
            rt[i] = rt[i - 1];
            scanf("%d %d", &a, &b);
            int id_ra = finds(i, a), id_rb = finds(i, b);
            if(tree[id_ra].f != tree[id_rb].f) printf("0\n");
            else printf("1\n");
        }
    }
    return 0;
}

/*

  Rejoicing in hope, patient in tribulation .

*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值