可持久化线段树(To the moon)

理解:可持久化线段树,对一组数据建立多棵线段树来维护其状态,在建立新的线段树时可以合用不变的部分以减小内存开销。多棵线段树能确保将多种状态保存,互不影响。
To the moon
题意:给一组数,m次操作:
C l r d 将l ~ r区间的数加上d,操作一次时间t加一。
Q l r 查询l ~ r区间和。
H l r t 查询第t次l ~ r区间和。
B t 回到第t次,t次之后的作废;
这道题在处理线段树lazy标记时没有将其向下压,因为为了不对前面线段树产生影响只能在每一个更改区间新建树才可以,内存开销很大。
因此要在查询的时候改变一下;

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cmath>
#define ll long long
#define lson mp[root].l
#define rson mp[root].r
using namespace std;
const int N=1e5+55;
int pos[N],a[N];  //pos[]记录每一次新建线段树的根节点;
int tot;   //tot全局变量来建树,感觉这种方法建树和一般线段树建树本质没有区别,只是为了能方便扩大线段树,这种更灵活,那种更简洁;
struct node{
    int l,r;
    ll sum,val;   //开long long ;
}mp[N*40];
void up_(int root, int l, int r)   //上传数据;
{
    int mid=(l+r)>>1;
    ll len1=mid-l+1,len2=r-mid;
    mp[root].sum=mp[lson].sum+mp[rson].sum+mp[lson].val*len1+mp[rson].val*len2;
}
void build_(int root, int l, int r)  //初始化建树;
{
    if(l==r){
        mp[root].sum=a[l];
        mp[root].val=0;
        return ;
    }
    mp[root].sum=0;
    mp[root].val=0;
    mp[root].l=tot++;   //扩大线段树;
    mp[root].r=tot++;
    int mid=(l+r)>>1;
    build_(mp[root].l,l,mid);
    build_(mp[root].r,mid+1,r);
    up_(root,l,r);
}
void update(int root, int l, int r, int s, int t, int pre, ll val)//区间增加;
{
    mp[root]=mp[pre];   //这里面和一般线段树区别就是新节点的建立;
    if(l==s&&r==t){
        mp[root].val+=val;
        return ;
    }
    int mid=(l+r)>>1;
    if(t<=mid){
        mp[root].l=tot++;
        update(mp[root].l,l,mid,s,t,mp[pre].l,val);
    }else if(s>mid){
        mp[root].r=tot++;
        update(mp[root].r,mid+1,r,s,t,mp[pre].r,val);
    }else{
        mp[root].l=tot++;
        update(mp[root].l,l,mid,s,mid,mp[pre].l,val);
        mp[root].r=tot++;
        update(mp[root].r,mid+1,r,mid+1,t,mp[pre].r,val);
    }
    up_(root,l,r);
}
ll query(int root, int l, int r, int s, int t, ll val)  //查询原理和一般线段树一样;
{
    if(l==s&&r==t){
        return mp[root].sum+(val+mp[root].val)*(r-l+1);
    }
    int mid=(l+r)>>1;
    if(t<=mid){
        return query(mp[root].l,l,mid,s,t,val+mp[root].val);
    }else if(s>mid){
        return query(mp[root].r,mid+1,r,s,t,val+mp[root].val);
    }else{
        return query(mp[root].l,l,mid,s,mid,val+mp[root].val)
        +query(mp[root].r,mid+1,r,mid+1,t,mp[root].val+val);
    }
}
int main()
{
    int n,m,l,r,t;
    ll val;
    char str[10];
    while(scanf("%d%d",&n,&m)!=EOF){
        tot=0;
        int cnt=0;
        for(int i=1;i<=n;++i) scanf("%d",&a[i]);
        pos[0]=tot++;
        build_(pos[0],1,n);
        for(int i=0;i<m;++i){
            scanf("%s",str);
            if(str[0]=='C'){
                scanf("%d%d%lld",&l,&r,&val);
                pos[++cnt]=tot++;
                update(pos[cnt],1,n,l,r,pos[cnt-1],val);
            }else if(str[0]=='Q'){
                scanf("%d%d",&l,&r);
                printf("%lld\n",query(pos[cnt],1,n,l,r,0));
            }else if(str[0]=='H'){
                scanf("%d%d%d",&l,&r,&t);
                printf("%lld\n",query(pos[t],1,n,l,r,0));
            }else{
                scanf("%d",&t); //这儿回到第t次相当于回到第t棵树;
                cnt=t;
                tot=pos[t+1];
            }
        }
    }
    return 0;
}

慢慢来,坚持;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值