理解:可持久化线段树,对一组数据建立多棵线段树来维护其状态,在建立新的线段树时可以合用不变的部分以减小内存开销。多棵线段树能确保将多种状态保存,互不影响。
To the moon
题意:给一组数,m次操作:
C l r d 将l ~ r区间的数加上d,操作一次时间t加一。
Q l r 查询l ~ r区间和。
H l r t 查询第t次l ~ r区间和。
B t 回到第t次,t次之后的作废;
这道题在处理线段树lazy标记时没有将其向下压,因为为了不对前面线段树产生影响只能在每一个更改区间新建树才可以,内存开销很大。
因此要在查询的时候改变一下;
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cmath>
#define ll long long
#define lson mp[root].l
#define rson mp[root].r
using namespace std;
const int N=1e5+55;
int pos[N],a[N]; //pos[]记录每一次新建线段树的根节点;
int tot; //tot全局变量来建树,感觉这种方法建树和一般线段树建树本质没有区别,只是为了能方便扩大线段树,这种更灵活,那种更简洁;
struct node{
int l,r;
ll sum,val; //开long long ;
}mp[N*40];
void up_(int root, int l, int r) //上传数据;
{
int mid=(l+r)>>1;
ll len1=mid-l+1,len2=r-mid;
mp[root].sum=mp[lson].sum+mp[rson].sum+mp[lson].val*len1+mp[rson].val*len2;
}
void build_(int root, int l, int r) //初始化建树;
{
if(l==r){
mp[root].sum=a[l];
mp[root].val=0;
return ;
}
mp[root].sum=0;
mp[root].val=0;
mp[root].l=tot++; //扩大线段树;
mp[root].r=tot++;
int mid=(l+r)>>1;
build_(mp[root].l,l,mid);
build_(mp[root].r,mid+1,r);
up_(root,l,r);
}
void update(int root, int l, int r, int s, int t, int pre, ll val)//区间增加;
{
mp[root]=mp[pre]; //这里面和一般线段树区别就是新节点的建立;
if(l==s&&r==t){
mp[root].val+=val;
return ;
}
int mid=(l+r)>>1;
if(t<=mid){
mp[root].l=tot++;
update(mp[root].l,l,mid,s,t,mp[pre].l,val);
}else if(s>mid){
mp[root].r=tot++;
update(mp[root].r,mid+1,r,s,t,mp[pre].r,val);
}else{
mp[root].l=tot++;
update(mp[root].l,l,mid,s,mid,mp[pre].l,val);
mp[root].r=tot++;
update(mp[root].r,mid+1,r,mid+1,t,mp[pre].r,val);
}
up_(root,l,r);
}
ll query(int root, int l, int r, int s, int t, ll val) //查询原理和一般线段树一样;
{
if(l==s&&r==t){
return mp[root].sum+(val+mp[root].val)*(r-l+1);
}
int mid=(l+r)>>1;
if(t<=mid){
return query(mp[root].l,l,mid,s,t,val+mp[root].val);
}else if(s>mid){
return query(mp[root].r,mid+1,r,s,t,val+mp[root].val);
}else{
return query(mp[root].l,l,mid,s,mid,val+mp[root].val)
+query(mp[root].r,mid+1,r,mid+1,t,mp[root].val+val);
}
}
int main()
{
int n,m,l,r,t;
ll val;
char str[10];
while(scanf("%d%d",&n,&m)!=EOF){
tot=0;
int cnt=0;
for(int i=1;i<=n;++i) scanf("%d",&a[i]);
pos[0]=tot++;
build_(pos[0],1,n);
for(int i=0;i<m;++i){
scanf("%s",str);
if(str[0]=='C'){
scanf("%d%d%lld",&l,&r,&val);
pos[++cnt]=tot++;
update(pos[cnt],1,n,l,r,pos[cnt-1],val);
}else if(str[0]=='Q'){
scanf("%d%d",&l,&r);
printf("%lld\n",query(pos[cnt],1,n,l,r,0));
}else if(str[0]=='H'){
scanf("%d%d%d",&l,&r,&t);
printf("%lld\n",query(pos[t],1,n,l,r,0));
}else{
scanf("%d",&t); //这儿回到第t次相当于回到第t棵树;
cnt=t;
tot=pos[t+1];
}
}
}
return 0;
}
慢慢来,坚持;