可持久化数组
模板链接
顾名思义,就是可以查询历史版本的数组,我们使用主席树维护这个数组就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;
}