【HDU - 4348】To the moon(主席树在线区间更新)

BUPT2017 wintertraining(15) #8G

题意

给一个数组a,有n个数,m次操作。\(N, M ≤ 10^5, |A i| ≤ 10^9, 1 ≤ l ≤ r ≤ N, |d| ≤ 10^4\)
操作有四种,
C l r d:更新区间[l,r],值都加上d,并且时间前进1
Q l r:查询[l,r]的区间和
H l r t:查询t时刻的区间和
B t:回到t时刻(一定是历史时刻),且t时刻之后的操作都作废了。

题解

这道题的关键是怎么更新区间和。
本来线段树更新区间和要push down也就是把懒惰标记下移,但是这样,整个修改区间都需要新建线段树的节点,空间不够。
所以考虑在查询的时候传参数下去,也就是一路加上大区间的lazy值,这样就不用push down了。

代码

#include <cstdio>
#include <cstring>
#define N 100005
#define M (N*25)
typedef long long ll;
namespace PST{
    int T[N],lson[M],rson[M],tot,n;
    ll sum[M], lz[M];
    void init(int _n){
        tot=0;
        n=_n;
        memset(sum,0,sizeof sum);
        memset(lz,0,sizeof lz);
    }
    void push_up(int root,int l,int r){
        sum[root]=sum[lson[root]]+sum[rson[root]]+lz[root]*(r-l+1);
    }
    int build(int l,int r){
        int root=++tot;
        if(l==r){
            scanf("%lld",sum+tot);
            return tot;
        }
        int mid=l+r>>1;
        lson[root]=build(l,mid);
        rson[root]=build(mid+1,r);
        push_up(root,l,r);
        return root;
    }
    int update(int root,int l,int r,int L,int R,int val){
        int newroot=++tot;
        lson[newroot]=lson[root];
        rson[newroot]=rson[root];
        lz[newroot]=lz[root];
        if(L<=l&&r<=R){
            sum[newroot]=sum[root]+(ll)val*(r-l+1);
            lz[newroot]+=val;
            return newroot;
        }
        int mid=l+r>>1;
        if(L<=mid)
            lson[newroot]=update(lson[root],l,mid,L,R,val);
        if(R>mid)
            rson[newroot]=update(rson[root],mid+1,r,L,R,val);
        push_up(newroot,l,r);
        return newroot;
    }
    ll query(int root,int l,int r,int L,int R,ll add){
        if(L<=l&&r<=R){
            return sum[root]+(add)*(r-l+1);
        }
        int mid=l+r>>1;
        ll ans=0;
        if(L<=mid)
            ans+=query(lson[root],l,mid,L,R,lz[root]+add);
        if(R>mid)
            ans+=query(rson[root],mid+1,r,L,R,lz[root]+add);
        return ans;
    }
}
int main(){
    int n,m;
    while(~scanf("%d%d",&n,&m)){
        int now;
        PST::init(n);
        PST::T[now=0]=PST::build(1,n);
        for(int i=0;i<m;++i){
            char op;
            int l,r,t,d;
            scanf(" %c",&op);
            if(op=='Q'){
                scanf("%d%d",&l,&r);
                printf("%lld\n",PST::query(PST::T[now],1,n,l,r,PST::lz[PST::T[now]]));
            }else if(op=='H'){
                scanf("%d%d%d",&l,&r,&t);
                printf("%lld\n",PST::query(PST::T[t],1,n,l,r,PST::lz[PST::T[t]]));
            }else if(op=='B'){
                scanf("%d", &now);
                PST::tot=PST::T[now+1]-1;
            }else if(op=='C'){
                scanf("%d%d%d",&l,&r,&d);
                int tmp=now;
                PST::T[++now]=PST::update(PST::T[tmp],1,n,l,r,d);
            }
        }
    }
}

转载于:https://www.cnblogs.com/flipped/p/HDU4348.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值