poj3468 树状数组(区间更新)

此题详解参见:poj 3468 树状数组解法

此篇写下自己之前不是很理解的地方。

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

#define MAXN 100005

__int64 initSum[MAXN];
__int64 t1[MAXN], t2[MAXN];

inline __int64 lowbit(__int64 x) {
    return x & -x;
}

inline void update(__int64 a[], int x, __int64 c) {
    while (x <= MAXN) {
        a[x] += c;
        x += lowbit(x);
    }
}

inline __int64 getSum(__int64 a[], int x) {
    __int64 sum = 0;
    while (x) {
        sum += a[x];
        x -= lowbit(x);
    }
    return sum;
}

//根据公式Sum[x] = SUM(A[i], i=1...x)+(x+1)*SUM(delta[i],i=1...x)-SUM(i*delta[i],i=1...x)求Sum[x]
__int64 Sum(__int64 t1[], __int64 t2[], int x) {
    __int64 res = initSum[x];
    res += (x+1) * getSum(t1, x);
    res -= getSum(t2, x);
    return res;
}

int main()
{
    int i, n, q, tmp;

    memset(initSum, 0, sizeof (initSum));
    memset(t1, 0, sizeof (t1));
    memset(t2, 0, sizeof (t2));
    scanf ("%d%d", &n, &q);
    for (i = 1; i <= n; i++) {
        scanf ("%d", &tmp);
        initSum[i] = initSum[i-1] + tmp;
    }
    while (q--) {
         __int64 sum_a, sum_b;
         char ch;
         int a, b, c;
         getchar();
         scanf ("%c", &ch);
         if (ch == 'Q') {
            scanf ("%d%d", &a, &b);
            sum_a = Sum(t1, t2, a-1);
            sum_b = Sum(t1, t2, b);
            printf("%I64d\n", sum_b-sum_a);
         }
         else {
            scanf ("%d%d%d", &a, &b, &c);

            //Sum[x] = SUM(A[i], i=1...x)+(x+1)*SUM(delta[i],i=1...x)-SUM(i*delta[i],i=1...x)
            //在成段更新时,是对delta数组进行更新,于是用两个树状数组t1,t2来维护
            //两个前缀和SUM(delta[i],i=1...x),SUM(i*delta[i],i=1...x),
            //而SUM(A[i], i=1...x)在更新过程中是不变的,所以可以预处理在数组中
            //将区间的成段更新转化成对t1,t2两个树状数组的单点更新,使更新复杂度保持O(log(n))
            update(t1, a, c); //更新SUM(delta[i],i=1...x)中的delta[a]
            update(t2, a, a*c);//更新SUM(i*delta[i],i=1...x)中的delta[a]
                              //这两个update后,区间A[a...n]每个数都增加了c。

            update(t1, b+1, -c);
            update(t2, b+1, -(b+1)*c);
                             //这两个update后,区间A[b+1...n]每个数都减少了c
                             //完成对区间A[a...b]的更新
         }
    }
    return 0;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值