树状数组进阶

树状数组进阶

​ 众所周知,线段树是可以进行区间修改的,那么与其类似的数据结构——树状数组是否也可以完成这个操作呢?

​ 有没有办法让区间修改变为单点修改呢?

差分

引入差分数组 b i = a i − a i − 1 b_i = a_i - a_{i-1} bi=aiai1
则有以下公式:
a i = ∑ j = 1 i b j a_i = \sum_{j=1}^i b_j ai=j=1ibj
s i = ∑ j = 1 i a i = ∑ j = 1 i ∑ k = 1 j b k s_i = \sum_{j=1}^i a_i=\sum_{j=1}^i \sum_{k=1}^j b_k si=j=1iai=j=1ik=1jbk
交换 ∑ \sum 顺序,
s i = ∑ k = 1 i ∑ j = k i b k = ∑ k = 1 i ( i − ( k − 1 ) ) × b k = i × ∑ k = 1 i b k − ∑ k = 1 i ( k − 1 ) × b k s_i = \sum_{k=1}^i \sum_{j=k}^i b_k=\sum_{k=1}^i (i-(k-1)) \times b_k=i \times \sum_{k=1}^i b_k-\sum_{k=1}^i (k-1) \times b_k si=k=1ij=kibk=k=1i(i(k1))×bk=i×k=1ibkk=1i(k1)×bk
可见,现在只需要维护两个前缀和就可以完成了

题目

P3368 【模板】树状数组 2

code

//#pragma GCC optimize (2)
//#pragma G++ optimize (2)
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <iostream>
#define G getchar
#define nxt(x) (x & (- x))
using namespace std;

int read()
{
    char ch;
    for(ch = G();(ch < '0' || ch > '9') && ch != '-';ch = G());
    int n = 0 , w;
    if (ch == '-')
    {
        w = -1;
        ch = G();
    } else w = 1;
    for(;'0' <= ch && ch <= '9';ch = G())n = (n<<1)+(n<<3)+ch-48;
    return n * w;
}

const int N = 500005;
int a[N] , b[N] , n , m , v , op , l , r;
long long s1[N] , s2[N];

void ins (int x , int v)
{
    long long vv = (long long) (x - 1) * v;
    for (;x <= n; x = x + nxt(x))
    {
        s1[x] = s1[x] + v;
        s2[x] = s2[x] + vv;
    }
}

long long sum (int x)
{
    long long s = 0;
    int X = x;
    for (; x ; x = x - nxt(x))
        s = s + s1[x] * X - s2[x];
    return s;
}

int main()
{
    freopen("1.in","r",stdin);
    //freopen("1.out","w",stdout);
    n = read();
    m = read();

    for (int i = 1; i <= n ; ++i)
    {
        a[i] = read();
        b[i] = a[i] - a[i-1];
        ins(i , b[i]);
    }

    for (int i = 0; i < m; ++i)
    {
        op = read();
        if (op == 1)
        {
            l = read();
            r = read();
            v = read();
            ins(l , v);
            ins(r + 1 , -v);
        }
        else
        {
            l = read();
            printf("%lld\n", sum(l) - sum(l-1));
        }
    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值