题目链接:http://poj.org/problem?id=3468
题目大意:给出一组数组v[i],有两种操作,一种给出两个数a,b。要求输出v[a]到v[b]之间的和。还有一种给出三个数a,b,c,让v[a]到v[b]之间的数全都加上c。
全然是树状数组可以实现的功能,可是假设就这样单纯的套用模板,做另外一种操作是更新每一个值,这种操作就有可能超时。
换一种思路,既然另外一种操作是给某区间上的全部数加上同样的值,那么应该是可以简化的才对。
如果数组sum[i]为原数组从v[1]到v[i]的和。数组c1[i]为更新之后,v[i]到v[n]的添加量。分析一下结果ans:
a,b之间的和ans=sum[b]-sum[a-1]+c1[1]*x + c1[2]*(x-1) + c1[3]*(x-2)+...+c1[x]*1
ans=sum[b]-sum[a-1]+segema(c1[i]*(x-i+1))
ans=sum[b]-sum[a-1] + (x+1)*segma(c1[i]) - segma(c1[i]*i)
令c2[i]=c1[i]*i;
如此便能利用树状数组解出此题
代码:
#include <stdio.h> #define N 100001 #define lowbit(i) ( i & (-i) ) int n; __int64 v[N]; __int64 c1[N];// 每一个C数组代表v[i-lowbit(i)+1]到v[i]之间的和 __int64 c2[N]; __int64 sum[N]; void Updata(__int64 *array,__int64 i,__int64 a) { for(;i<=n;i+=lowbit(i)) array[i]+=a; } __int64 Sumv(__int64 *array,__int64 i) //求出数组v[1]到v[i]的和 { __int64 result=0; while (i>=1) { result+=array[i]; i-=lowbit(i); } return result; } int main() { __int64 q,i=0; __int64 ans=0; scanf("%I64d%I64d",&n,&q); for(i=1;i<=n;i++) scanf("%I64d",&v[i]); for(__int64 i=1;i<=n;i++) sum[i]=sum[i-1]+v[i]; while(q--) { char ch[2]; scanf("%s",ch); if(ch[0]=='Q') { __int64 s,t; scanf("%I64d%I64d",&s,&t); ans=sum[t]-sum[s-1]; ans+=((t+1)*Sumv(c1,t)-Sumv(c2,t)); ans-=(s*Sumv(c1,s-1)-Sumv(c2,s-1)); printf("%I64d ",ans); } else { __int64 a,b,c; scanf("%I64d%I64d%I64d",&a,&b,&c); Updata(c1,a,c); Updata(c1,b+1,-c); Updata(c2,a,c*a); Updata(c2,b+1,-c*(b+1)); } } return 0; }