树状数组的区间修改和区间查询问题

本文章适合已经会基本的树状数组用法看。

树状数组最经常的两个操作:单点更新,区间求和
如果要区间更新怎么办?
参考文章:https://www.cnblogs.com/kickit/p/9172189.html

(问题一)给定数组 a,有 2 种操作
  1. 将区间 [l,r] 的 a 加上 v
  2. 求区间 ∑ i = l r a i \sum_{i=l}^{r}a_{i} i=lrai

解法上面博客已经说明,就不再加以说明。

(问题二)给定数组a,b;有 2 种操作,
  1. 将区间 [l,r] 的 a 加上 v

  2. ∑ i = l r a i ∗ b i \sum_{i=l}^{r}a_{i}*b_{i} i=lraibi

分析:
该问题直接差分是不行的,因为多了一个权值b,然而因为要进行区间修改操作,我们依然可以先化成差分的形式。
d i = a i − a i − 1 d_{i}=a_{i}-a_{i-1} di=aiai1

∑ i = 1 x a i ∗ b i \sum_{i=1}^{x}a_{i}*b_{i} i=1xaibi = ∑ i = 1 x ∑ j = 1 i d j ∗ b i \sum_{i=1}^{x}\sum_{j=1}^{i}d_{j}*b_{i} i=1xj=1idjbi

这个式子直观上不太好看,我考虑将他拆开(数学太差,只能靠肉眼观察QaQ)

= b 1 ( d 1 ) + b 2 ( d 1 + d 2 ) + . . + b x ( d 1 + d 2 + . . + d x ) b_{1}(d_{1})+b_{2}(d_{1}+d_{2})+..+b_{x}(d_{1}+d_{2}+..+d_{x}) b1(d1)+b2(d1+d2)+..+bx(d1+d2+..+dx)

肉眼发现将 d 提出来会得到一个有意思的东西

= d 1 ( b 1 + b 2 + . . + b x ) + d 2 ( b 2 + . . + b x ) + . . + d x ( b x ) d_{1}(b_{1}+b_{2}+..+b_{x})+d_{2}(b_{2}+..+b_{x})+..+d_{x}(b_{x}) d1(b1+b2+..+bx)+d2(b2+..+bx)+..+dx(bx)

s i = ∑ j = 1 i b j s_{i} = \sum_{j=1}^{i}b_{j} si=j=1ibj

上式

= ∑ i = 1 x d i ∗ ( s x − s i − 1 ) \sum_{i=1}^{x}d_{i}*(s_{x}-s_{i-1}) i=1xdi(sxsi1)

= ∑ i = 1 x d i ∗ s x − ∑ i = 1 x d i ∗ s i − 1 \sum_{i=1}^{x}d_{i}*s_{x} - \sum_{i=1}^{x}d_{i}*s_{i-1} i=1xdisxi=1xdisi1

= s x ∑ i = 1 x d i − ∑ i = 1 x d i ∗ s i − 1 s_{x}\sum_{i=1}^{x}d_{i} - \sum_{i=1}^{x}d_{i}*s_{i-1} sxi=1xdii=1xdisi1

则该式子和问题1的式子变得一模一样,该问题就解决了。

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+5;
int a[N],s[N],val1[N],val2[N],n;
//树状数组部分, val1维护d , val2维护 di * si-1
inline int lowbit(int x){
    return x&(-x);
}
void update(int x,int v,int *val){
    for(int i=x;i<=n;i+=lowbit(i))
        val[i]+=v;
}
int sum(int x,int *val){
    int ans=0;
    for(int i=x;i;i-=lowbit(i))
        ans+=val[i];
    return ans;
}
int main(){

    scanf("%d",&n);
    for(int i=1;i<=n;i++){  //设a的初始值为0,这里输入的是b
        int x;
        scanf("%d",&s[i]);
        s[i]+=s[i-1];
    }
    int m;
    scanf("%d",&m);
    while(m--){
        int op,x,y,v;
        scanf("%d%d%d",&op,&x,&y);
        if(op==1) {
            scanf("%d",&v);
            update(x,v,val1);
            update(y+1,-v,val1);
            update(x,v*s[x-1],val2);
            update(y+1,-v*s[y],val2);
        } else {
            int ans;
            ans = s[y]  *sum(y,val1)   - sum(y,val2);
            ans-= s[x-1]*sum(x-1,val1) - sum(x-1,val2);
            printf("%d\n",ans);
        }

    }
    return 0;
}
/**给出一组测试数据
6
1 2 3 4 5 6
4
1 2 3 3
2 3 4
1 1 5 3
2 2 6
*/

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值