题意:
给定长度为n的序列,m次操作,一开始的时间戳为0
操作有四种:
(1)C l r d:区间[l,r]中的数都加1,同时当前的时间戳加1 。
(2)Q l r:查询当前时间戳区间[l,r]中所有数的和 。
(3)H l r t:查询时间戳t区间[l,r]的和 。
(4)B t:将当前时间戳置为t 。即回到过去t时刻。
数据范围:n,m<=1e5
解法:
显然可持久化,
但是涉及区间修改操作,所以要将标记永久化,而不能pushdown,因为pushdown需要新建的区间点太多。
标记永久化就是给节点打上一个区间修改标记tag,但是不下传,
当查询的区间[st,ed]完全被当前区间[l,r]包含时,tag可以用于统计答案
和平常线段树不太一样的地方:
1.因为[st,ed]需要完全被[l,r]包含,所以线段树的写法和平常不太一样,是拆区间的写法,
线段树常见写法:
if(st<=mid)update(st,ed,val,l,mid,node*2);
if(ed>mid)update(st,ed,val,mid+1,r,node*2+1);
标记永久化必须用的写法:
if(ed<=mid)update(st,ed,val,l,mid,node*2);
else if(st>mid)update(st,ed,val,mid+1,r,node*2+1);
else update(st,mid,val,l,mid,node*2),update(mid+1,ed,val,mid+1,r,node*2+1);
因为要时刻保证[st,ed]被[l,r]完全包含
2.因为每个区间节点都有一个tag,不同区间节点之间的tag是相互独立、没有联系的,
所以pushup的写法也不一样,
例如现在要维护区间和,操作为区间加法,那么pushup是这样的:
void pushup(int k,int l,int r){//k是节点编号
sum[k]=sum[lc[k]]+sum[rc[k]]+add[k]*(r-l+1);
}
每次的add标记都要计算进去,因为和普通laz标记不一样,没有被下传然后清空
-----分割线-----
暂时就这么多
code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxm=1e5+5;
int lc[maxm*40],rc[maxm*40],sum[maxm*40],add[maxm*40];
int rt[maxm],tot;
int now;
//
int a[maxm];
int n,m;
void init(){
tot=0;
now=0;
}
void pushup(int k,int l,int r){
sum[k]=sum[lc[k]]+sum[rc[k]]+add[k]*(r-l+1);
}
void build(int l,int r,int &k){
k=++tot;
lc[k]=rc[k]=add[k]=0;
if(l==r){
sum[k]=a[l];
return ;
}
int mid=(l+r)/2;
build(l,mid,lc[k]);
build(mid+1,r,rc[k]);
pushup(k,l,r);
}
void update(int st,int ed,int val,int l,int r,int last,int &k){
k=++tot;
//
lc[k]=lc[last],rc[k]=rc[last];
sum[k]=sum[last],add[k]=add[last];
//
if(st==l&&ed==r){
sum[k]+=val*(ed-st+1);
add[k]+=val;
return ;
}
int mid=(l+r)/2;
if(ed<=mid)update(st,ed,val,l,mid,lc[last],lc[k]);
else if(st>mid)update(st,ed,val,mid+1,r,rc[last],rc[k]);
else update(st,mid,val,l,mid,lc[last],lc[k]),update(mid+1,ed,val,mid+1,r,rc[last],rc[k]);
pushup(k,l,r);
}
int ask(int st,int ed,int l,int r,int k){
if(!k)return 0;
if(st==l&&ed==r)return sum[k];
int mid=(l+r)/2;
int ans=add[k]*(ed-st+1);//这时候[st,ed]一定被[l,r]全包含
if(ed<=mid){
ans+=ask(st,ed,l,mid,lc[k]);
}else if(st>mid){
ans+=ask(st,ed,mid+1,r,rc[k]);
}else{
ans+=ask(st,mid,l,mid,lc[k])+ask(mid+1,ed,mid+1,r,rc[k]);
}
return ans;
}
signed main(){
ios::sync_with_stdio(0);
while(cin>>n>>m){
init();
for(int i=1;i<=n;i++)cin>>a[i];
build(1,n,rt[now]);
while(m--){
char op;cin>>op;
if(op=='C'){
int l,r,d;cin>>l>>r>>d;
now++;
update(l,r,d,1,n,rt[now-1],rt[now]);
}else if(op=='Q'){
int l,r;cin>>l>>r;
int ans=ask(l,r,1,n,rt[now]);
cout<<ans<<endl;
}else if(op=='H'){
int l,r,t;cin>>l>>r>>t;
int ans=ask(l,r,1,n,rt[t]);
cout<<ans<<endl;
}else if(op=='B'){
int x;cin>>x;
tot=rt[x+1]-1;
now=x;
}
}
}
return 0;
}