树状数组区间和、区间加减

题面

题目
在这里插入图片描述
在这里插入图片描述

分析

如果用线段树做那就是裸题了,但是用树状数组我们需要转化一下;

首先看到区间加,我们考虑使用差分

对于差分数组来说,维护区间加是很容易的,只需要单点修改;

那么我们看看区间和有什么规律;

比如求区间 [ L , R ] [L,R] [L,R]的和,则是 a L + . . . + a R a_L+...+a_R aL+...+aR

那么对于差分数组b来说,则说 ∑ i = L R ∑ j = 1 i \sum_{i=L}^{R} \sum_{j=1}^{i} i=LRj=1i;

这个是暴力求法,会TLE的;

我们将这个式子画出来,见下图,黑色表示真实要加的,红色表示填充上去多余的;

在这里插入图片描述
如果求区间 [ L , R ] [L,R] [L,R]直接代入上面那个式子的话,不太好求;

但是我们可以求出 [ 1 , R ] [1,R] [1,R] [ 1 , L − 1 ] [1,L-1] [1,L1],相减即可,也就是前缀和的方式;

如果将上面式子的 L = 1 , R = x L=1,R=x L=1R=x

可以得到式子 ( x + 1 ) ∗ ∑ i = 1 x b i − ∑ i = 1 x i ∗ b i (x+1)*\sum_{i=1}^x b_i - \sum_{i=1}^x i*b_i (x+1)i=1xbii=1xibi

那么我们可以维护两个树状数组,其中 t r 1 维 护 差 分 数 组 b i , t r 2 维 护 i ∗ b i tr1维护差分数组b_i,tr2维护i*b_i tr1bitr2ibi即可;

至于单点修改就很好理解了,对于tr1来说直接操作即可;

对于tr2来说, 将 ( b i = b i + v ) 代 入 ( i ∗ b i ) 将(b_i = b_i+v)代入(i*b_i) (bi=bi+v)(ibi)可得 i ∗ b i + i ∗ v i*b_i+i*v ibi+iv

因此只需要加上 i ∗ v i*v iv即可

Code

#include <iostream>

using namespace std;

const int N = 1e5+10;

typedef long long ll;

int n,m,a[N],b[N];
ll tr1[N],tr2[N];

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

ll query(ll tr[],int x){
    ll ret = 0;
    for(int i=x;i;i-=lowbit(i)){
        ret += tr[i];
    }
    return ret;
}
void add(ll tr[],int u,ll v){
    for(int x=u;x<=n;x+=lowbit(x)){
        tr[x] += v;
    }
}

ll prefix_sum(int x){
    return (x+1) * query(tr1,x) - query(tr2,x);
}
int main(){
    cin >> n >> m;
    for(int i=1;i<=n;++i){
        cin >> a[i];
        b[i] = a[i] - a[i-1];
        add(tr1,i,b[i]);
        add(tr2,i,1ll*i*b[i]);
    }
    char ch;
    int l,r,d;
    while(m--){
        cin >> ch;
        if(ch == 'Q'){
            cin >> l >> r;
            cout << prefix_sum(r) - prefix_sum(l-1) << '\n';
        }else{
            cin >> l >> r >> d;
            add(tr1,l,d);add(tr1,r+1,-d);
            add(tr2,l,l*d);add(tr2,r+1,(r+1)*-d);
        }
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值