目录
- tags
- 中文题面
- 思路
- 代码
tags
线段树
主席树
中文题面
如题,你需要维护这样的一个长度为 N 的数组,支持如下几种操作
在某个历史版本上修改某一个位置上的值
访问某个历史版本上的某一位置的值
此外,每进行一次操作(对于操作2,即为生成一个完全一样的版本,不作任何改动),就会生成一个新的版本。版本编号即为当前操作的编号(从1开始编号,版本0表示初始状态数组)
输入格式
输入的第一行包含两个正整数 N N N, M M M, 分别表示数组的长度和操作的个数。
第二行包含 N N N 个整数,依次为初始状态下数组各位的值(依次为 a i a_i ai, 1 ≤ i ≤ N 1≤i≤N 1≤i≤N)。
接下来 M M M 行每行包含3或4个整数,代表两种操作之一( i i i 为基于的历史版本号):
对于操作1,格式为 v i 1 l o c i v a l u e i v_i\ 1\ loc_i\ value_i vi 1 loci valuei,即为在版本 v i v_i vi 的基础上,将 a [ l o c i ] a[loc_i] a[loci] 修改为 v a l u e i value_i valuei。
对于操作2,格式为 v i 2 l o c i v_i\ 2\ loc_i vi 2 loci,即访问版本 v i v_i vi 中的 a [ l o c i ] a[loc_i] a[loci] 的值,注意:生成一样版本的对象应为 v i v_i vi。
输出格式
输出包含若干行,依次为每个操作2的结果。
1
≤
N
,
M
≤
106
,
1
≤
l
o
c
i
≤
N
,
0
≤
v
i
<
i
,
−
1
0
9
≤
a
i
,
v
a
l
u
e
i
≤
1
0
9
1≤N,M≤106,1≤loc_i≤N,0≤v_i<i,−10^9≤a_i,value_i≤10^9
1≤N,M≤106,1≤loci≤N,0≤vi<i,−109≤ai,valuei≤109
思路
如果对于每次修改都复制一遍数组,空间复杂度为
O
(
N
M
)
O(NM)
O(NM),显然不合适。考虑到每次修改只变了数组的一小部分,我们考虑对这一小部分进行复制。把数组按照线段树的方式划分,得到以下结构:
我们发现修改箭头所指的值时,只有黄线标记的节点受到了影响,这些点的数量为
l
o
g
n
log n
logn,只需每次对这些节点进行复制,其他未修改的点保留指向即可。
注:主席树一般开40倍空间
代码
#include <bits/stdc++.h>
using namespace std;
int cnt = 0, a[1000005], root[1000005], n, m;
int ver, op, loc, value;
struct node {
int l, r, val;
} tree[40000005];
int clone(int cp) {
tree[++cnt] = tree[cp];
return cnt;
}
int build(int now, int l, int r) {
if (l == r) {
tree[now].val = a[l];
return now;
}
int mid = l + r >> 1;
tree[now].l = build(++cnt, l, mid);
tree[now].r = build(++cnt, mid + 1, r);
return now;
}
int update(int now, int l, int r, int x, int v) {
int newnew = clone(now);
if (l == r) {
tree[newnew].val = v;
return newnew;
}
int mid = l + r >> 1;
if (x > mid) tree[newnew].r = update(tree[newnew].r, mid + 1, r, x, v);
else tree[newnew].l = update(tree[newnew].l, l, mid, x, v);
return newnew;
}
int query(int now, int l, int r, int x) {
if (l == r) return tree[now].val;
int mid = l + r >> 1;
if (x > mid) return query(tree[now].r, mid + 1, r, x);
return query(tree[now].l, l, mid, x);
}
int main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> a[i];
root[0] = build(++cnt, 1, n);
for (int i = 1; i <= m; i++) {
cin >> ver >> op >> loc;
if (op == 1) {
cin >> value;
root[i] = update(root[ver], 1, n, loc, value);
} else {
cout << query(root[ver], 1, n, loc) << '\n';
root[i] = root[ver];
}
}
return 0;
}