树状数组的区间修改与区间查询

我们知道,线段树实现区间修改和查询可以通过lazy标记来实现

今天学到一个新姿势,树状数组也可以实现区间修改和查询

我们引入delta数组delta[i]表示区间[i, n]的共同增量 于是修改区间[l, r]时修改delta[l]和delta[r + 1]即可
(就是差分的思路)
查询的时候是查询区间 [l, r] 的和 即sum[r] - sum[l - 1] 所以现在的问题是求sum[i]

sum[i] = a[1]+...+a[i] + delta[1]*i + delta[2]*(i - 1) + delta[3]*(i - 2)+...+delta[i]*1   // a[i]为原始数组
       = sigma( a[x] ) + sigma( delta[x]  *  (i + 1 - x) )
       = sigma( a[x] ) + (i + 1) * sigma( delta[x] ) - sigma( delta[x] * x )

其中 sigma( a[x] ) 是可以预处理出来的 于是只需要维护 delta[x] 与 delta[x] * x 的前缀和(作为两个树状数组就可以了)

#include <cstdio>
#include <iostream>

#define lowbit(i) (i & (-i))

using namespace std;

int readint()
{
    int sign = 1, n = 0; char c = getchar();
    while(c < '0' || c > '9'){ if(c == '-') sign = -1; c = getchar(); }
    while(c >= '0' && c <= '9') { n = n*10 + c-'0'; c = getchar(); }
    return sign*n;
}

const int Nmax = 200100;

int N, Q;

long long delta[Nmax]; // delta的前缀和 
long long deltai[Nmax]; // delta * i的前缀和 
long long sum[Nmax]; // 原始前缀和

long long Query(long long *array, int pos)
{
    long long temp = 0ll;
    while(pos > 0)
    {
        temp += array[pos];
        pos -= lowbit(pos);
    }
    return temp;
}

void Update(long long *array, int pos, int x)
{
    while(pos <= N)
    {
        array[pos] += x;
        pos += lowbit(pos);
    }
}

int main()
{
    N = readint();

    for(int i = 1; i <= N; ++i)
    {
        int x = readint();
        sum[i] = sum[i - 1] + x;
    }

    Q = readint();

    while(Q--)
    {
        int sign = readint();
        if(sign == 1) // 修改:把[l, r]区间均加上x 
        {
            int l = readint(), r = readint(), x = readint();
            Update(delta, l, x);
            Update(delta, r+1, -x);
            Update(deltai, l, x * l);
            Update(deltai, r+1, -x * (r+1));
        } 
        else // 查询:[l, r]区间和 
        {
            int l = readint(), r = readint(); 
            long long suml = sum[l - 1] + l * Query(delta, l - 1) - Query(deltai, l - 1);
            long long sumr = sum[r] + (r + 1) * Query(delta, r) - Query(deltai, r);
            printf("%lld\n", sumr - suml);
        }
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值