2021.08.10 可持久化线段树
学了之后发现也没多难,就是一种把所有历史版本存下来的线段树。
实现是基于这条优秀的性质:每次操作影响的结点只有log2N个。基于此性质,我们每次修改的时候就把线段树上每个需要修改的结点进行克隆(clone)。对于被克隆的结点,其左右结点中的一个需要继续递归下去修改,另一个则不用(因为是单点修改),就保留不需要修改的那个点的关系,然后递归下去克隆需要修改的结点,最后递归返回出来再把相应的指针连到这一结点的克隆。用root[i]记录第i个版本新增结点的根。
此处再用堆式线段树写法就不方便了,尝试了新的写法。
另外,注意空间是O(NlogN)的,大概是20*N左右,这里开了32*N。
贴代码,代码对应的模板是洛谷的 P3919 【模板】可持久化线段树 1(可持久化数组):
#include <bits/stdc++.h> using namespace std; const int N=1e6+11; const int M=1e6+11; int n,m,a[N]; struct ChairmanTree { int l,r,sum; }t[N<<5]; int root[M],top; int clone(int now) { t[++top]=t[now]; return top; } void update(int now) { t[now].sum=0; if(t[now].l) t[now].sum+=t[t[now].l].sum; if(t[now].r) t[now].sum+=t[t[now].r].sum; } int buildtree(int l,int r) { int now=++top; if(l==r) { t[now].sum=a[l]; return now; } int mid=(l+r)/2; t[now].l=buildtree(l,mid); t[now].r=buildtree(mid+1,r); update(now); return now; } int modify(int now,int l,int r,int pos,int val) { now=clone(now); if(l==r) { t[now].sum=val; return now; } int mid=(l+r)/2; if(pos<=mid) t[now].l=modify(t[now].l,l,mid,pos,val); else t[now].r=modify(t[now].r,mid+1,r,pos,val); update(now); return now; } int query(int now,int l,int r,int pos) { if(l==r) return t[now].sum; int mid=(l+r)/2; if(pos<=mid) return query(t[now].l,l,mid,pos); else return query(t[now].r,mid+1,r,pos); } int main() { int i,opt,ver,pos,val; scanf("%d%d",&n,&m); for(i=1;i<=n;i++) scanf("%d",&a[i]); root[0]=buildtree(1,n); for(i=1;i<=m;i++) { scanf("%d%d%d",&ver,&opt,&pos); if(opt==1) { scanf("%d",&val); root[i]=modify(root[ver],1,n,pos,val); } else { printf("%d\n",query(root[ver],1,n,pos)); root[i]=clone(root[ver]); } } return 0; }