洛谷 P3919 【模板】可持久化线段树 1(可持久化数组)
P3919 【模板】可持久化线段树 1(可持久化数组)
题目背景
2021.9.18 增添一组 hack 数据 by @panyf。
标题即题意。
有了可持久化数组,便可以实现很多衍生的可持久化功能。(例如:可持久化并查集)
题目描述
如题,你需要维护这样的一个长度为 $ N $ 的数组 \(a\),支持如下两种操作:
-
在某个历史版本上修改某一个位置上的值。
-
访问某个历史版本上的某一位置的值。
此外,每进行一次操作,就会生成一个新的版本。版本编号即为当前操作的编号(从 \(1\) 开始编号,版本 \(0\) 表示初始状态数组)。
对于操作 \(2\),即为生成一个完全一样的版本,不作任何改动。即,询问生成的版本是询问所访问的那个版本的复制。
输入格式
输入的第一行包含两个正整数 $ N, M $,分别表示数组的长度和操作的个数。
第二行包含 $ N $ 个整数,其中第 \(i\) 个数为 \(a_i\)。
接下来 $ M $ 行每行包含 \(3\) 或 \(4\) 个整数,代表两种操作之一(设当前是第 \(i\) 次操作):
-
对于操作 \(1\),格式为
v 1 p c,即为在版本 $ v $ 的基础上,将 $ a_{p} $ 修改为 \(c\)。 -
对于操作 \(2\),格式为
v 2 p,即访问版本 $ v $ 中的 $ a_{p} $ 的值,注意:此时应生成与版本 \(v\) 完全相同的新版本。
输出格式
输出包含若干行,依次为每个操作 \(2\) 的结果。
输入输出样例 #1
输入 #1
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
输出 #1
59
87
41
87
88
46
说明/提示
数据规模
对于 \(30\%\) 的数据,$ 1 \leq N, M \leq {10}^3 $。
对于 \(50\%\) 的数据,$ 1 \leq N, M \leq {10}^4 $。
对于 \(70\%\) 的数据,$ 1 \leq N, M \leq {10}^5 $。
对于 \(100\%\) 的数据:
- $ 1 \leq N, M \leq {10}^6$;
- \(1 \leq p \leq N\);
- 设当前是第 \(x\) 次操作,\(0 \leq v < x\);
- \(-{10}^9 \leq a_i,c \leq {10}^9\)。
样例说明
所有操作结束后,总共生成了 \(11\) 个版本,编号为 \(0 \sim 10\),依次为:
版本 \(0\):59 46 14 87 41,
版本 \(1\):59 46 14 87 41,
版本 \(2\):14 46 14 87 41,
版本 \(3\):57 46 14 87 41,
版本 \(4\):88 46 14 87 41,
版本 \(5\):88 46 14 87 41,
版本 \(6\):59 46 14 87 41,
版本 \(7\):59 46 14 87 41,
版本 \(8\):88 46 14 87 41,
版本 \(9\):14 46 14 87 41,
版本 \(10\):59 46 14 87 91。
算法分析
首先,这是一题模板题,就是可持久化线段树,非常像线段树模板,但这是主席树。主席树就是记录历史时刻的树,修改要新建节点,记录每一次修改了哪些节点,然后剩下的就是普通的线段树了。
AC代码
#include<bits/stdc++.h>
using namespace std;
struct node{//一个节点的信息
int l,r,val;//左儿子,右儿子,权值
};
int n,m;
int cnt = 0;
int a[1000005];
int r[32000005];
node t[32000005];
int make(int pos){//创新节点
t[++cnt] = t[pos];//传递信息
return cnt;
}
int build(int l,int r,int pos){//建树
pos = ++cnt;
if(l==r){
t[pos].val = a[l];
return cnt;
}
int mid = (l+r)>>1;
t[pos].l = build(l,mid,t[pos].l);
t[pos].r = build(mid+1,r,t[pos].r);
return pos;
}
int update(int index,int val,int l,int r,int pos){//修改
pos = make(pos);//修改要新建节点!!!
if(l==r){
t[pos].val = val;
return pos;
}
int mid = (l+r)>>1;
if(index<=mid){
t[pos].l = update(index,val,l,mid,t[pos].l);
}else{
t[pos].r = update(index,val,mid+1,r,t[pos].r);
}
return pos;
}
int query(int index,int l,int r,int pos){//查询
if(l==r){
return t[pos].val;
}
int mid = (l+r)>>1;
if(index<=mid){
return query(index,l,mid,t[pos].l);
}else{
return query(index,mid+1,r,t[pos].r);
}
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
for(int i = 1; i<=n; i++){
cin>>a[i];
}
r[0] = build(1,n,0);//记录原来的节点
for(int i = 1; i<=m; i++){
int v,op,p,c;
cin>>v>>op>>p;
if(op==1){
cin>>c;
r[i] = update(p,c,1,n,r[v]);
}else{
cout<<query(p,1,n,r[v])<<'\n';
r[i] = r[v];//更新节点
}
}
return 0;
}
总结
本题就是一个模板题,只需要去学一下可持久化线段树就行了,确实可以评蓝题。

浙公网安备 33010602011771号