[luogu3919]可持久化数组【主席树】

时间:2023-02-01 04:28:35

链接:https://www.luogu.org/problemnew/show/P3919

分析

很明显我们可以用主席树来维护,所谓主席树就是可持久化线段树,能够查询历史版本而且可以实现修改操作,反正就是复制了一遍。其原理就是动态开点复制前驱版本,在复制版本中做我们需要的所有的操作,然后我们需要记录每一个节点的根节点,表示线段树是哪一个历史版本。

ac代码

#include <bits/stdc++.h>
#define N 1000005
using namespace std;
struct Last_segment_tree {
    #define mid ((l+r)>>1)
    struct node {
        int lc, rc, s;//记录我们的左右儿子
        node() {}
    }tr[N*20];//开大一点没有问题,开小了就wa掉了
    int tot;
    Last_segment_tree() {//构造函数,也就是赋初值的意思
        tot = 0;
    }
    void build(int &nod, int l, int r, int *a) {//取地址的原因和动态开点线段树一样,我们需要记录左右儿子的标号
        nod = ++ tot;
        if (l == r) {
            tr[nod].s = a[l];
            return;
        }
        build(tr[nod].lc, l, mid, a);
        build(tr[nod].rc, mid + 1, r, a);
    }
    void update(int &nod, int pre, int l, int r, int k, int v) {
        nod = ++ tot;
        tr[nod] = tr[pre];
        if (l == r) {
            tr[nod].s = v;
            return;
        }
        if (k <= mid) update(tr[nod].lc, tr[pre].lc, l, mid, k, v);
        else update(tr[nod].rc, tr[pre].rc, mid + 1, r, k, v);
    }
    int query(int nod, int l, int r, int k) {
        if (l == r) return tr[nod].s;
        if (k <= mid) return query(tr[nod].lc, l, mid, k);
        else return query(tr[nod].rc, mid + 1, r, k);
    }
}lstr;
int a[N], rt[N];
int n, m;
int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i ++) scanf("%d", &a[i]);
    lstr.build(rt[0], 1, n, a);
    for (int i = 1; i <= m; i ++) {
        int pre, opt, loc, val;
        scanf("%d%d%d", &pre, &opt, &loc);
        if (opt == 1) {
            scanf("%d", &val);
            lstr.update(rt[i], rt[pre], 1, n, loc, val);
        }
        else {
            printf("%d\n", lstr.query(rt[pre], 1, n, loc));
            rt[i] = rt[pre];//题目中说的这个也算是一个历史版本
        }
    }
    return 0;
}