这道题做过很多遍了,一开始用线段树去搞,然后学了伸展树,又用伸展树去写了一遍,如今发现树状数组也可以写
这里涉及到树状数组的区间更新问题
树状数组能够求的一定是前缀和的形式,区间更新必须转换为端点的单点更新才能实现前缀和的修改,那么此时修需要对存储的数据进行一些变形。
先来推一下公式
令 di=ai-ai-1;
将值ai用前缀差值和来表示,则有ax=sum(d1 + d2 + ...dx)
此时前缀和Sx= sum(a1+...ax)=sum( sum( d1 )+ sum(d1+d2)+...sum(d1+d2+,,,dx) )=x*sum(d1+d2+,,,dx)-sum( (i-1)*di ) (i=2~x)
此时可以观察到前缀和只剩下di 和 di*(I-1) 两个东西了,
那么在树状数组里面用两个数组维护这两个值就可以实现树状区间更新了
具体操作见代码
#include<cstring> #include<cstdio> #include<iostream> #include<algorithm> using namespace std; #define maxn 100004 int n,m; long long c1[maxn],c2[maxn]; long long a[maxn]; inline int lowbit(int x) { return x&-x; } inline void update(int index,int x,long long val) { if(index==0) for(int i=x;i<=n;i+=lowbit(i)) c1[i]+=val; else for(int i=x;i<=n;i+=lowbit(i)) c2[i]+=(x-1)*val; } inline long long query(int x) { long long sum1=0,sum2=0; for(int i=x;i>0;i-=lowbit(i)) sum1+=c1[i],sum2+=c2[i]; return x*sum1-sum2; } void init() { a[0]=0; memset(c1,0,sizeof c1); memset(c2,0,sizeof c2); for(int i=1;i<=n;i++) { long long d=a[i]-a[i-1]; update(0,i,d); update(1,i,d); } } int main() { while(scanf("%d%d",&n,&m)!=EOF) { for(int i=1;i<=n;i++) { scanf("%lld",&a[i]); } init(); while(m--) { char op[3]; scanf("%s",op); if(op[0]=='Q') { int l,r; scanf("%d%d",&l,&r); long long ans=query(r)-query(l-1); printf("%lld\n",ans); } else { int l,r; long long x; scanf("%d%d%lld",&l,&r,&x); update(0,l,x); update(0,r+1,-x); update(1,l,x); update(1,r+1,-x); } } } return 0; }