链接: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;
}