题意:给你一个数列,每次询问一个区间的和,或者每次将一个区间的所有元素都加上一个数
一 算法
树状数组天生用来动态维护数组前缀和,其特点是每次更新一个元素的值,查询只能查数组的前缀和,
但这个题目求的是某一区间的数组和,而且要支持批量更新某一区间内元素的值,怎么办呢?实际上,
还是可以把问题转化为求数组的前缀和。
首先,看更新操作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
然后来看查询操作query(s, t),求A[s]...A[t]的区间和,转化为求前缀和,设sum[i] = A[1]+...+A[i],则
A[s]+...+A[t] = sum[t] - sum[s-1],
那么前缀和sum[x]又如何求呢?它由两部分组成,一是数组的原始和,二是该区间内的累计增量和, 把数组A的原始
值保存在数组org中,并且delta[i]对sum[x]的贡献值为delta[i]*(x+1-i),那么
sum[x] = org[1]+...+org[x] + delta[1]*x + delta[2]*(x-1) + delta[3]*(x-2)+...+delta[x]*1
= org[1]+...+org[x] + segma(delta[i]*(x+1-i))
= segma(org[i]) + (x+1)*segma(delta[i]) - segma(delta[i]*i),1 <= i <= x
=segma(org[i]-delta[i]*i)+(x+1)*delta[i], i<=1<=x //by huicpc0207 修改 这里就可以转化为两个个数组
这其实就是三个数组org[i], delta[i]和delta[i]*i的前缀和,org[i]的前缀和保持不变,事先就可以求出来,delta[i]和
delta[i]*i的前缀和是不断变化的,可以用两个树状数组来维护。
Time Limit: 9000/3000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 5515 Accepted Submission(s): 2950
3 1 1 2 2 3 3 3 1 1 1 2 1 3 0
1 1 1 3 2 1区间更新,单点求值,read()返回值即单点值
#include<iostream> using namespace std; int tree[100100]; int maxn; void update(int k,int v) { while(k<=maxn) { tree[k]+=v; k+=k&-k; } } int read(int k)//求区间1到k的和值 { int sum=0; while(k>0) { sum+=tree[k]; k-=k&-k; } return sum; } int main() { int i,a,b,temp; while(scanf("%d",&maxn)&&maxn) { temp=maxn; memset(tree,0,sizeof(tree)); while(temp--) { scanf("%d%d",&a,&b); update(a,1); update(b+1,-1); } for(i=1;i<=maxn;i++) { printf("%d",read(i)); if(i!=maxn) printf(" "); else printf("/n"); } } return 0; }