可持久化数组及并查集(概念及模板)

可持久化数组

模板链接
顾名思义,就是可以查询历史版本的数组,我们使用主席树维护这个数组就ok了.从此延伸出了可持久化并查集等很多可持久化结构,比较简单,
下面是模板代码:

#include <iostream>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
#include <algorithm>
#include <cstdio>
#include <cmath>
#define ll long long
const int N = 1e6+5;
const int inf = 0x3f3f3f3f;
int cnt;
struct Node
{
    int l, r, val;
}tr[N<<4];
int su[N];
int build(int l, int r)
{
    int p = ++cnt;
    if (l == r)
    {tr[p].val = su[l]; return p;}
    int mid = (l + r) >> 1;
    tr[p].l = build(l ,mid);
    tr[p].r = build(mid+1, r);
    return p;
}
int change(int p, int l, int r, int x, int val)
{
    int cur = ++cnt;
    tr[cur] = tr[p];
    if (l == r)
    {tr[cur].val = val; return cur;}
    int mid = (l + r) >> 1;
    if (x <= mid) tr[cur].l = change(tr[cur].l, l, mid, x, val);
    else tr[cur].r = change(tr[cur].r, mid+1, r, x, val);
    return cur;
}
int ask(int p, int l, int r, int x)
{
    if (l == r) return tr[p].val;
    int mid = (l + r) >> 1;
    if (x <= mid) return ask(tr[p].l, l, mid, x);
    else return ask(tr[p].r, mid+1, r, x);
}
int root[N];
int main()
{
    int n, m;
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) scanf("%d", &su[i]);
    root[0] = build(1, n);
    for (int i = 1; i <= m; i++)
    {
        int rt, op, x, y;
        scanf("%d%d%d", &rt, &op, &x);
        if (op == 1)
        {
            scanf("%d", &y); root[i] = change(root[rt], 1, n, x, y);
        }
        else
        {
            printf("%d\n", ask(root[rt], 1, n, x)); root[i] = root[rt];
        }
    }
    return 0;
}
可持久化并查集

模板链接
可持久化并查集无法进行路径压缩.寻根时只能暴力跳祖先,(ps,当然也可以每压缩一次就开一个新版本,本人实测洛谷mle…)为了进行优化,我们合并的时候可以优先挂深度小的点,这样能减缓时间上的压力(ps,本人觉得也只能这样了,但是神奇的是,居然跑这么快)据题解某大佬说这是启发式合并,emmmmm,那怪不得了,启发式合并是本人见过的最神奇的算法,
下面是模板代码:

#include <iostream>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
#include <algorithm>
#include <cstdio>
#include <cmath>
#define ll long long
using namespace std;
const int N = 5e6+5;
const int inf = 0x3f3f3f3f;
int cnt;
struct Node
{
    int l, r, val;
}tr[N<<4];
int fa[N], dep[N];
int build(int l, int r)
{
    int p = ++cnt;
    if (l == r)
    {tr[p].val = fa[l]; return p;}
    int mid = (l + r) >> 1;
    tr[p].l = build(l ,mid);
    tr[p].r = build(mid+1, r);
    return p;
}
int change(int p, int l, int r, int x, int val)
{
    int cur = ++cnt;
    tr[cur] = tr[p];
    if (l == r)
    {tr[cur].val = val; dep[cur] = dep[p]; return cur;}
    int mid = (l + r) >> 1;
    if (x <= mid) tr[cur].l = change(tr[cur].l, l, mid, x, val);
    else tr[cur].r = change(tr[cur].r, mid+1, r, x, val);
    return cur;
}
void update(int p, int l, int r, int pos)
{
    if (l == r) {dep[p]++; return;}
    int mid = (l + r) >> 1;
    if (pos <= mid) update(tr[p].l, l, mid, pos);
    else update(tr[p].r, mid+1, r, pos);
}
int ask(int p, int l, int r, int x)
{
    if (l == r) return tr[p].val;
    int mid = (l + r) >> 1;
    if (x <= mid) return ask(tr[p].l, l, mid, x);
    else return ask(tr[p].r, mid+1, r, x);
}
int root[N];
int n, m;
int fi(int x, int sta)
{
    int fax = ask(sta, 1, n, x);
    if (x ==fax) return x;
    int q = fi(fax, sta);
    return q;
}
int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) fa[i] = i;
    root[0] = build(1, n);
    for (int i = 1; i <= m; i++)
    {
        int op, x, y;
        scanf("%d%d", &op, &x);
        if (op == 1)
        {
            scanf("%d", &y);
            int _x = fi(x, root[i-1]), _y = fi(y, root[i-1]);
            if (_x == _y) {root[i] = root[i-1]; continue;}
            if (dep[_x] > dep[_y]) swap(_x, _y);
            root[i] = change(root[i-1], 1, n, _x, _y);
            if (dep[_x] == dep[_y]) update(root[i], 1, n, _y);
        }
        else if (op == 2)
        {
            root[i] = root[x];
        }
        else
        {
            scanf("%d", &y);
            root[i] = root[i-1];
            int _x = fi(x, root[i]), _y = fi(y, root[i]);
            if (_x == _y) puts("1");
            else puts("0");
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值