分析:
如果想看历史版本 那就可以对每个操作复制 将未进行操作的线段树复制 再对原线段树修改
但这样肯定不行
O
(
n
m
)
O(nm)
O(nm) 而且每次开一棵线段树空间早爆炸
那肯定只要对进行了操作的节点进行复制
红色记录修改的节点 对于每个新节点 编号就是此时新增的总结点数
+
1
+1
+1
并且 每个节点可能不止
1
1
1个父节点 所以不能像线段树一样
×
2
\times 2
×2 和
×
2
+
1
\times 2+1
×2+1 就要分别记录左右儿子
CODE:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<vector>
#include<bitset>
#define Ctnue continue
//#pragma GCC optimize(2)
#define reg register
using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
const int N=1e6+5;
struct node{
int l,r,val;
}tree[N*20];
int n,m,tot=0,root[N*20],a[N];
int add(int x)
{
tree[++tot]=tree[x]; //全部信息传递到 新节点
return tot;
}
int build(int x,int l,int r)
{
x=++tot; //新建节点的编号
if(l==r){
tree[x].val=a[l];
return tot;
}
int mid=(l+r)>>1;
tree[x].l=build(tree[x].l,l,mid);
tree[x].r=build(tree[x].r,mid+1,r);
return x;
}
int update(int x,int l,int r,int p,int val)
{
x=add(x);
if(l==r){
tree[x].val=val;
}else
{
int mid=(l+r)>>1;
if(p<=mid)
tree[x].l=update(tree[x].l,l,mid,p,val);
else
tree[x].r=update(tree[x].r,mid+1,r,p,val);
}
return x;
}
int query(int x,int l,int r,int p)
{
if(l==r){
return tree[x].val;
}else
{
int mid=(l+r)>>1;
if(p<=mid)
return query(tree[x].l,l,mid,p);
else
return query(tree[x].r,mid+1,r,p);
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
root[0]=build(0,1,n); //表示第0版本的根编号
for(int i=1;i<=m;i++)
{
int k,op,x;
scanf("%d%d%d",&k,&op,&x);
if(op==1)
{
int y;
scanf("%d",&y);
root[i]=update(root[k],1,n,x,y);
}
if(op==2)
{
printf("%d\n",query(root[k],1,n,x));
root[i]=root[k];
}
}
return 0;
}