题目链接http://acm.hdu.edu.cn/showproblem.php?pid=4348
题意
- C l r d:时间标记加一,区间[l,r]的数增加d
- Q l r:询问当前[l,r]的和
- H l r t:询问t时刻,区间[l,r]的和
- B t:回到时间t
题解
区间更新主席树模板题
区间更新线段树主要有两个写法:一是lazy标记下传,二是lazy标记永久化。
如果要对主席树里的线段树区间更新,用前者需要对更新到的每一处都开一个点来维护,空间上会被卡。
而标记永久化就可以很轻松的维护。对于每段更新的区间[L,R],没有完全包含[L,R]的区间都直接更新值。包含的区间打上lazy标记,查询的时候路过每个标记都记录在查询的答案里。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+7;
int n,m,stamp;
ll t[N*40],lz[N*40];
int ls[N*40],rs[N*40];
int rt[N],tot;
ll a[N];
void bd(int &now,int l,int r){
now=++tot;
lz[now]=0;
if(l==r){t[now]=a[l];return;}
int m=l+r>>1;
bd(ls[now],l,m);
bd(rs[now],m+1,r);
t[now]=t[ls[now]]+t[rs[now]];
}
void upd(int &now,int pre,int l,int r,int L,int R,ll v){
now=++tot;
t[now]=t[pre];lz[now]=lz[pre];
ls[now]=ls[pre];rs[now]=rs[pre];
t[now]+=(min(r,R)-max(l,L)+1)*v;
if(L<=l&&r<=R){lz[now]+=v;return;}
int m=l+r>>1;
if(L<=m) upd(ls[now],ls[pre],l,m,L,R,v);
if(m<R) upd(rs[now],rs[pre],m+1,r,L,R,v);
}
ll que(int now,int l,int r,int L,int R){
if(L<=l&&r<=R) return t[now];
ll res=(min(r,R)-max(L,l)+1)*lz[now];
int m=l+r>>1;
if(L<=m) res+=que(ls[now],l,m,L,R);
if(m<R) res+=que(rs[now],m+1,r,L,R);
return res;
}
char s[10];
int main(){
while(scanf("%d%d",&n,&m)!=EOF){
tot=0;stamp=0;
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
}
bd(rt[0],1,n);
for(int i=1;i<=m;i++){
scanf("%s",s);
if(s[0]=='C'){
int l,r,d;
scanf("%d%d%d",&l,&r,&d);
stamp++;
upd(rt[stamp],rt[stamp-1],1,n,l,r,d);
}
else if(s[0]=='Q'){
int l,r;
scanf("%d%d",&l,&r);
printf("%lld\n",que(rt[stamp],1,n,l,r));
}
else if(s[0]=='H'){
int l,r,tt;
scanf("%d%d%d",&l,&r,&tt);
printf("%lld\n",que(rt[tt],1,n,l,r));
}
else if(s[0]=='B'){
scanf("%d",&stamp);
}
}
}
}