可持续化数组
1.算法分析
3.典型例题
P3919 【模板】可持久化线段树 1(可持久化数组)
题意: 如题,你需要维护这样的一个长度为 N 的数组,支持如下几种操作
-
在某个历史版本上修改某一个位置上的值: 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} aloci修改为 v a l u e i {value}_i valuei
-
访问某个历史版本上的某一位置的值: 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} aloci的值,生成一样版本的对象应为 v i vi vi
此外,每进行一次操作(对于操作2,即为生成一个完全一样的版本,不作任何改动),就会生成一个新的版本。版本编号即为当前操作的编号(从1开始编号,版本0表示初始状态数组) 1 < = n , m < = 1 0 6 , 1 < = l o c i < = N , 0 < = v i < i , − 1 e 9 < = a i , v a l u e i < = 1 e 9 1<=n,m<=10^6,1<=loc_i<=N,0<=v_i<i,-1e9<=a_i,value_i<=1e9 1<=n,m<=106,1<=loci<=N,0<=vi<i,−1e9<=ai,valuei<=1e9
题解:
代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
int a[N];
struct Node {
int l, r,
val; // l和r为左右子树的节点,val为记录的数字,但是val只有叶节点才使用
} hjt[N * 40];
int cnt, root[N]; // cnt为计数变量,root[i]记录版本号为i的线段树的根节点
// 建树
void build(int l, int r, int &now) {
now = ++cnt; // 生成新结点
if (l == r) // 如果到叶节点
{
hjt[now].val = a[l]; // 赋值
return;
}
int m = (l + r) >> 1; // 求中点
build(l, m, hjt[now].l); // 建左子树
build(m + 1, r, hjt[now].r); // 建右子树
}
// 修改操作
void modify(int l, int r, int ver, int &now, int &pos, int &num) {
hjt[now = ++cnt] = hjt[ver]; // 复制一个原来的版本
if (l == r) // 修改叶节点
{
hjt[now].val = num;
return;
}
int m = (l + r) >> 1; // 中点
if (pos <= m)
modify(l, m, hjt[ver].l, hjt[now].l, pos,
num); // 如果pos小于等于中点,说明在左子树,修改左子树
else
modify(m + 1, r, hjt[ver].r, hjt[now].r, pos, num); // 修改右子树
}
// 询问操作
int query(int l, int r, int now, int &pos) {
if (l == r) return hjt[now].val; // 到达叶节点
int m = (l + r) >> 1; // 求中点
if (pos <= m)
return query(l, m, hjt[now].l,
pos); // 如果要查询的位置比中点小,查询左子树
else
return query(m + 1, r, hjt[now].r, pos); // 否则查询右子树
}
int main(int argc, char const *argv[]) {
int n, m, ver, opt, x, y;
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> a[i]; // 读入数字
build(1, n, root[0]); // 建树,0为初始化版本
for (int i = 1; i <= m; i++) // 读入询问
{
cin >> ver >> opt;
switch (opt) {
case 1:
cin >> x >> y;
modify(1, n, root[ver], root[i], x, y); // 修改
break;
case 2:
cin >> x;
cout << query(1, n, root[ver], x) << endl; // 查询
root[i] = root[ver]; // 记录新版本
break;
}
}
return 0;
}