AcWing 243. 一个简单的整数问题2 树状数组 + 懒人标记

给定一个长度为 N 的数列 A,以及 M 条指令,每条指令可能是以下两种之一:

C l r d,表示把 A[l],A[l+1],…,A[r] 都加上 d。
Q l r,表示询问数列中第 l∼r 个数的和。
对于每个询问,输出一个整数表示答案。

输入格式
第一行两个整数 N,M。

第二行 N 个整数 A[i]。

接下来 M 行表示 M 条指令,每条指令的格式如题目描述所示。

输出格式
对于每个询问,输出一个整数表示答案。

每个答案占一行。

数据范围
1≤N,M≤105,
|d|≤10000,
|A[i]|≤109
输入样例:

10 5
1 2 3 4 5 6 7 8 9 10
Q 4 4
Q 1 10
Q 2 4
C 3 6 3
Q 2 4

输出样例:

4
55
9
15

这道题是经典的线段树模型 , 线段树只能单点修改 和 区间查询, 所以我们要用到懒人标记来实现区间修改(懒人标记的含义是,子树还没有修改)

代码如下:

#include<iostream>

using namespace std;

const int N = 1e5 + 10;

typedef long long LL;

struct Node{
    int  l , r;
    LL sum , add;
} tr[4 * N];

int n , m ;
LL w[N] ;

void pushup(int u){
    tr[u].sum = tr[ u << 1 ].sum + tr[u << 1 | 1].sum;
}

void pushdown(int u){
    auto &root = tr[u];
    auto &left = tr[u << 1];
    auto &right = tr[u << 1 | 1];
    if(root.add){
        left.sum += (left.r - left.l + 1) * root.add ;
        left.add += root.add;
        right.sum += (right.r - right.l + 1) * root.add ;
        right.add += root.add;
        root.add = 0;
    }
    
}

void build(int u , int l , int r){
    if( l == r ) tr[u] = { l , r , w[l] , 0 };
    else
    {
        int mid = l + r >> 1;
        tr[u] = { l , r };
        build( u << 1 , l , mid );
        build( u << 1 | 1 , mid + 1 , r);
        pushup(u);
        tr[u].add = 0;
    }
}

void modify(int u ,int l , int r , LL d ){
    if(tr[u].l >= l && tr[u].r <= r){
        tr[u].add += d;
        tr[u].sum += (tr[u].r - tr[u].l + 1) * d;
        return;
    }
    pushdown(u);
    int mid = tr[u].l + tr[u].r >> 1 ;
    if(l <= mid ) modify(u << 1 , l , r ,d );
    if(r > mid ) modify(u << 1 | 1 , l , r , d );
    pushup(u);
}

LL query(int u ,int  l , int r ){
    if(tr[u].l >= l && tr[u].r <= r) return tr[u].sum ;
    pushdown(u);
    LL sum = 0;
    int mid = tr[u].l + tr[u].r >> 1;
    if(l <= mid) sum += query(u << 1 , l , r );
    if(r > mid) sum += query(u << 1 | 1 , l , r );
    pushup(u);
    return sum;
}
int main(){
    cin >> n >> m;
    for(int i = 1 ; i <= n ; i ++ ) cin >> w[i] ;
    build(1 , 1 , n);
    while( m-- ){
        char op[2] ;
        int l , r;
        cin >> op >> l >>r;
        if(*op == 'Q'){
            cout << query(1 , l , r) << endl ;
        }
        else{
            LL d ;
            cin >> d;
            modify(1 , l , r , d );
        }
    }
}

树状数组的代码:

#include<iostream>

using namespace std;

const int N = 1e5 + 10;

typedef long long LL;

LL tr1[N];// b[i]
LL tr2[N];// b[i] * i

int n , m ;
LL w[N];

int lowbit(int x){
    return x & -x;
}

void add(LL tr[] , int x , LL d){
    for(int i = x ; i <= n ; i += lowbit(i) )
    tr[i] += d;
}

LL take(LL tr[] , int x ){
    LL sum = 0 ;
    for(int i = x ; i ; i -= lowbit(i) )
    sum += tr[i];
    return sum;
}

LL presum(int x){
    return take(tr1 , x) * (x + 1) - take(tr2 , x);
}

int main(){
    cin >> n >> m ;
    for(int i = 1 ; i <= n ; i++ ){
        cin >> w[i];
        int b = w[i] - w[i - 1];
        add(tr1 , i , b);
        add(tr2 , i , (LL)i * b);
    }
    while( m-- ){
        char op[2];
        int l , r ;
        cin >> op >> l >> r;
        if(*op == 'Q'){
            cout << presum(r) - presum(l - 1) << endl;
        }
        else{
            LL d;
            cin >> d;
            add( tr1 , l , d);
            add( tr2 , l , l * d);
            add( tr1 , r + 1 , - d );
            add( tr2 , r + 1 , - (r + 1) * d);
        }
    }

}


作者:月色
链接:https://www.acwing.com/activity/content/code/content/1276386/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值