题目:http://poj.org/problem?id=3468
算法来自:http://blog.csdn.net/non_cease/article/details/7435052
树状数组天生用来动态维护数组前缀和,其特点是每次更新一个元素的值,查询只能查数组的前缀和。但这个题目求的是某一区间的数组和,而且要支持批量更新某一区间内元素的值。所以这道题的关键就是成段更新树状数组的问题。
思路就是把问题转化为求数组的前缀和。
首先,看更新操作update(s, t, d)把区间A[s]...A[t]都增加d,我们引入一个数组delta[i],表示A[i]...A[n]的共同增量,n是数组的大小。那么update操作可以转化为:
1)令delta[s] = delta[s] + d,表示将A[s]...A[n]同时增加d,但这样A[t+1]...A[n]就多加了d,所以
2)再令delta[t+1] = delta[t+1] - d,表示将A[t+1]...A[n]同时减d
然后来看查询操作getSum(s, t),求A[s]...A[t]的区间和,转化为求前缀和,设sum[i] = A[1]+...+A[i],则
A[s]+...+A[t] = sum[t] - sum[s-1],
那么前缀和sum[x]又如何求呢?它由两部分组成,一是数组的原始和,二是该区间内的累计增量和, 把数组A的原始和保存在数组sum中,并且delta[i]对sum(1...x)的贡献值为delta[i]*(x+1-i),那么
sum(1...x) = A[1]+...+A[x] + delta[1]*x + delta[2]*(x-1) + delta[3]*(x-2)+...+delta[x]*1
= A[1]+...+A[x] + segma(delta[i]*(x+1-i))
= segma(A[i]) + (x+1)*segma(delta[i]) - segma(delta[i]*i),1 <= i <= x
= sum[i] + (x+1)*segma(delta[i]) - segma(delta[i]*i),1 <= i <= x
这其实就是三个数组[i], delta[i]和delta[i]*i的前缀和,A[i]的前缀和保持不变,事先就可以求出来存在sum[]中,delta[i]和delta[i]*i的前缀和是不断变化的,可以用两个树状数组来维护,分别设为delta1和delta2。
代码如下:
注意易错的地方"%I64d",无论是输出还是录入时候
#include<iostream>
using namespace std;
#define maxn 100002
int array[maxn];
__int64 ori[maxn],delta1[maxn],delta2[maxn];
int n;
int lowbit(int t)
{
return t&(-t);
}
void add(__int64 tree[],int i,int num)
{
while(i <= n)
{
tree[i] += num;
i += lowbit(i);
}
}
__int64 getSum(__int64 tree[],int i)
{
__int64 ret = 0;
while(i > 0)
{
ret += tree[i];
i -= lowbit(i);
}
return ret;
}
int main()
{
int q,a,b,c;
char command;
__int64 sum;
scanf("%d %d",&n,&q);
memset(ori,0,sizeof(ori));
memset(delta1,0,sizeof(delta1));
memset(delta2,0,sizeof(delta2));
for(int i = 1;i<=n;i++){
scanf("%I64d",&ori[i]);
ori[i] += ori[i-1];
}
while(q--)
{
getchar();
scanf("%c %d %d",&command,&a,&b);
if(command == 'Q'){
sum = ori[b] - ori[a-1];
sum += ((b+1)*getSum(delta1,b) - a*getSum(delta1,a-1));
sum += (getSum(delta2,a-1) - getSum(delta2,b) );
//here is output
printf("%I64d\n",sum);
}else if(command == 'C'){
scanf("%d",&c);
add(delta1,a,c);
add(delta1,b+1,-c);
add(delta2,a,a*c);
add(delta2,b+1,(b+1)*(-c));
}
}
return 0;
}
1032MS