个人觉得主席树无法像普通线段树那样更新是因为 主席树很多结点都是共用的 假如在某个结点我们把lazy标记pushdown了 那么它可能会更新其他时间点的树 这样我们访问其他时间点的树的时候 会得到错误的答案
所以另类的更新是 我们不把lazy标记pushdown 而是在query操作的时候沿途累加lazy标记 当到达的区间在访问区间内时 返回当前结点的值+区间长度*累加的lazy
另外pushup操作我们也要稍微改一下 now.val=lson.val+rson.val+(r-l+1)*now.lazy
这样就能保证每次query操作都是正确的啦
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1e5+10;
typedef long long ll;
int rt[N],time,tot;
struct pre_tree{
int l,r;
ll val,lazy;
}tr[N*30];
ll a[N];
void pushup(int id,int l,int r){
tr[id].val=tr[tr[id].l].val+tr[tr[id].r].val+tr[id].lazy*1ll*(r-l+1);
}
void build(int &o,int l,int r){
o=++tot;
tr[o].lazy=0;
if(l==r){
tr[o].val=a[l];
return;
}
int mid = l+r>>1;
build(tr[o].l,l,mid);
build(tr[o].r,mid+1,r);
pushup(o,l,r);
}
void update(int &o,int las,int l,int r,int L,int R,ll val){
o=++tot;tr[o]=tr[las];
if(L<=l&&R>=r){
tr[o].val+=1ll*(r-l+1)*val;
tr[o].lazy+=val;
return;
}
int mid = l+r>>1;
if(L<=mid) update(tr[o].l,tr[las].l,l,mid,L,R,val);
if(R>mid) update(tr[o].r,tr[las].r,mid+1,r,L,R,val);
pushup(o,l,r);
}
ll query(int o,int l,int r,int L,int R,ll laz){
if(L<=l&&R>=r) return tr[o].val+1ll*(r-l+1)*laz;
ll ret = 0;
laz+=tr[o].lazy;
int mid = l+r>>1;
if(L<=mid) ret+=query(tr[o].l,l,mid,L,R,laz);
if(R>mid) ret+=query(tr[o].r,mid+1,r,L,R,laz);
return ret;
}
int main(){
int n,m,l,r,t;
ll d;
char op[3];
while(~scanf("%d%d",&n,&m)){
tot=time=0;
for(int i = 1; i <= n; i++) scanf("%lld",&a[i]);
build(rt[time],1,n);
for(int i = 1; i <= m; i++){
scanf("%s",op);
if(op[0]=='C'){
scanf("%d%d%lld",&l,&r,&d);
update(rt[time+1],rt[time],1,n,l,r,d);
time++;
}else if(op[0]=='Q'){
scanf("%d%d",&l,&r);
printf("%lld\n",query(rt[time],1,n,l,r,0));
}else if(op[0]=='H'){
scanf("%d%d%d",&l,&r,&t);
printf("%lld\n",query(rt[t],1,n,l,r,0));
}else{
scanf("%d",&time);
}
}
}
return 0;
}