【区间更新主席树/标记永久化】HDU - 4348 - To the moon

题目链接http://acm.hdu.edu.cn/showproblem.php?pid=4348


题意

  1. C l r d:时间标记加一,区间[l,r]的数增加d
  2. Q l r:询问当前[l,r]的和
  3. H l r t:询问t时刻,区间[l,r]的和
  4. 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);
            }
        }
    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值