主席树模版 记录历史操作

描述

给一个长为N的数列,有M次操作,每次操作是以下两种之一:
(1)修改数列中的一个数
(2)求某次操作后连续一段的和

输入

第一行两个正整数N和M。
第二行N的整数表示这个数列。
接下来M行,每行开头是一个字符,若该字符为’M’,则表示一个修改操作,接下来两个整数x和y,表示把x位置的数修改为y;若该字符为’Q’,则表示一个询问操作,接下来三个整数x、y、z,表示求数列中[x,y]这段区间在第z次操作后的和。

输出

对每一个询问操作单独输出一行,表示答案。

样例输入

5 4
1 2 3 4 5
Q 2 3 0
M 3 5
Q 2 3 2
Q 1 3 1

样例输出

5
7
6


#include <cstdio>
#include <iostream>
using namespace std;
const int N=1e5+7;
struct Node{
    int l, r, ls, rs, sum;
}tree[N*20];
int rt[N], cnt;
void pushup( int nd ){
    tree[nd].sum=tree[tree[nd].ls].sum+tree[tree[nd].rs].sum;
}
void build( int &nd, int l, int r ){
    nd=++cnt;
    tree[nd].l=l, tree[nd].r=r;
    if(l==r) {
        scanf("%d", &tree[nd].sum );
        return;
    }
    int mid=(l+r)>>1;
    build( tree[nd].ls, l, mid );
    build( tree[nd].rs, mid+1, r );
    pushup(nd);
}
void modify(int pre, int &nd, int p, int val ){
    nd=++cnt;
    tree[nd]=tree[pre];
    if( tree[nd].l==tree[nd].r ){
        tree[nd].sum=val;
        return;
    }
    int mid=(tree[nd].l+tree[nd].r)>>1;
    if( mid>=p ) modify( tree[pre].ls, tree[nd].ls, p, val );
    else modify( tree[pre].rs, tree[nd].rs, p, val );
    pushup(nd);
}
void update( int pre, int &nd){
    nd=++cnt;
    tree[nd]=tree[pre];
}
int query( int nd, int l, int r ){
    if( l<=tree[nd].l&&tree[nd].r<=r ){
        return tree[nd].sum;
    }
    int ret=0;
    int mid=(tree[nd].l+tree[nd].r)>>1;
    if( mid>=l ) ret+=query( tree[nd].ls, l, r );
    if( mid<r ) ret+=query( tree[nd].rs, l, r );
    return ret;
}
int main(){
    int n, m;
    scanf("%d%d", &n, &m );
    cnt=0;
    build( rt[0], 1, n );
    for ( int i=1; i<=m; i++ ){
        char ch[5];
        int x, y, z;
        scanf("%s", ch );
        if( ch[0]=='M' ){
            scanf("%d%d", &x, &y );
            modify( rt[i-1], rt[i], x, y );
        }else {
            scanf("%d%d%d", &x, &y, &z );
            printf("%d\n", query(rt[z], x, y) );
            update( rt[i-1], rt[i] );
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值