题目描述
维护这样的一个长度为 $n$ 的数组,支持如下几种操作
1. 在某个历史版本上修改某一个位置上的值
2. 访问某个历史版本上的某一位置的值
3.
此外,每进行一次操作(对于操作2,即为生成一个完全一样的版本,不作任何改动),就会生成一个新的版本。版本编号即为当前操作的编号(从1开始编号,版本0表示初始状态数组)
输入样例
5 10
59 46 14 87 41
0 2 1
0 1 1 14
0 1 1 57
0 1 1 88
4 2 4
0 2 5
0 2 4
4 2 1
2 2 2
1 1 5 91
输出样例
59
87
41
87
88
46
可持久化线段树
线段树里什么信息都不需要维护,只需要在叶节点存对应下标的值即可
时间复杂度 $O((n + m)logn$
C++ 代码
#include<cstdio>
inline int read(){
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
inline void write(int x){
static char F[200];
int tmp=x>0?x:-x;
if(x<0)putchar('-');
int cnt=0;
while(tmp>0){
F[cnt++]=tmp%10+'0';
tmp/=10;
}
while(cnt>0) putchar(F[--cnt]);
}
const int N = 1000010;
int n, m;
int a[N];
struct Node{
int l, r;
int val;
}tr[N * 40];
int root[N], tot;
void build(int &u, int l, int r)
{
u = ++ tot;
if(l == r)
{
tr[u].val = a[l];
return;
}
int mid = l + r >> 1;
build(tr[u].l, l, mid), build(tr[u].r, mid + 1, r);
}
void modify(int ver, int &u, int l, int r, int x, int val)
{
tr[u = ++ tot] = tr[ver];
if(l == r)
{
tr[u].val = val;
return;
}
int mid = l + r >> 1;
if(x <= mid) modify(tr[ver].l, tr[u].l, l, mid, x, val);
else modify(tr[ver].r, tr[u].r, mid + 1, r, x, val);
}
int query(int u, int l, int r, int x)
{
if(l == r) return tr[u].val;
int mid = l + r >> 1;
if(x <= mid) return query(tr[u].l, l, mid, x);
return query(tr[u].r, mid + 1, r, x);
}
int main()
{
n = read();
m = read();
for(int i = 1; i <= n; i ++) a[i] = read();
build(root[0], 1, n);
for(int i = 1; i <= m; i ++)
{
int v, op, x, val;
v = read();
op = read();
x = read();
if(op == 1)
{
val = read();
modify(root[v], root[i], 1, n, x, val);
}
else
{
write(query(root[v], 1, n, x));
putchar('\n');
root[i] = root[v];
}
}
return 0;
}