原题地址:https://www.luogu.org/problemnew/show/P3919
题目简述
维护一个长度为N的数组,支持如下几种操作:
- 在某个历史版本上修改某一个位置上的值
- 访问某个历史版本上的某一位置的值
此外,每进行一次操作(对于操作2,即为生成一个完全一样的版本,不作任何改动),就会生成一个新的版本。版本编号即为当前操作的编号(从1开始编号,版本0表示初始状态数组)
思路
首先感谢来自@zcysky的模板。写的非常漂亮,封装也很精致。
这题是裸题,于是直接上模板就行了。注意此题输入数据大,需要读入优化。
关于可持久化线段树的介绍与总结,之后把坑填上。
代码
#include<bits/stdc++.h>
const int N=1000005;
using namespace std;
int a[N],n,q,rt[N*20];//空间复杂度O(mlogn)
#define MAXB 50000000
char buf[MAXB],*cp=buf;
inline int read()
{
int f=1,x=0;
while(*cp<'0'||*cp>'9') {
if(*cp=='-')
f=-1;
cp++;
}
while(*cp>='0'&&*cp<='9') {
x=x*10+*cp-'0';
cp++;
}
return f*x;
}
struct Persistable_Segment_Tree {
int lc[N*20],rc[N*20],val[N*20],cnt;
inline void build(int &o,int l,int r){
o=++cnt;
if(l==r) {
val[o]=a[l];
return;
}
int mid=(l+r)>>1;
build(lc[o],l,mid);
build(rc[o],mid+1,r);
}
inline void ins(int &o,int pre,int l,int r,int q,int v){
o=++cnt;
lc[o]=lc[pre];
rc[o]=rc[pre];
val[o]=val[pre];
if(l==r) {
val[o]=v;
return;
}
int mid=(l+r)>>1;
if(q<=mid)
ins(lc[o],lc[pre],l,mid,q,v);
else
ins(rc[o],rc[pre],mid+1,r,q,v);
}
inline int query(int o,int l,int r,int q){//类似二分的查询
if(l==r)
return val[o];
int mid=(l+r)>>1;
if(q<=mid)
return query(lc[o],l,mid,q);
else
return query(rc[o],mid+1,r,q);
}
}T;
int main()
{
fread(buf,1,MAXB,stdin);
n=read();
int m=read();
for(int i=1;i<=n;i++)
a[i]=read();
T.build(rt[0],1,n);
for(int i=1;i<=m;i++){
int pre=read(),opt=read(),x=read();
if(opt==1) {//操作1:在版本pre的基础上将第x个数修改为v
int v=read();
T.ins(rt[i],rt[pre],1,n,x,v);
}
if(opt==2) {//操作2:访问版本pre中第x个数的值
printf("%d\n",T.query(rt[pre],1,n,x));
rt[i]=rt[pre];
}
}
}