您的位置:首页 > 理论基础 > 数据结构算法

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

2016-11-23 21:59 190 查看
我们知道,线段树实现区间修改和查询可以通过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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息