题目:
题解:
1.处理出原数组的差分数组,即可实现区间加;
2.首先观察:差分数组和原数组前缀和的元素之间的关系:
.......
观察可以得出:前缀和和差分数组的关系是:
这样前缀和就别转换为了两个差分前缀和的形式,只需要将差分数组用数组数组处理即可。
再通过一般前缀和求区间和的思想求出某一段之和。
ll premix_sum(int l,int r){
return (r+1)*sum(tr1,r)-sum(tr2,r)-(l*sum(tr1,l-1) - sum(tr2,l-1) );
}
代码:
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
const int N=100010;
int n,m;
int a[N];
ll b[N];
ll tr1[N];
ll tr2[N];
int lowbit(int x){
return x&-x;
}
ll sum(ll tr[],int x){
ll res=0;
for(int i=x;i;i-=lowbit(i))res+=tr[i];
return res;
}
void add(ll tr[],int a,ll b){
for(int i=a;i<=n;i+=lowbit(i))tr[i]+=b;
}
ll premix_sum(int l,int r){
return (r+1)*sum(tr1,r)-sum(tr2,r)-(l*sum(tr1,l-1) - sum(tr2,l-1) );
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",a+i);
for(int i=1;i<=n;i++)b[i]=a[i]-a[i-1];
for(int i=1;i<=n;i++)add(tr1,i,b[i]);
for(int i=1;i<=n;i++)add(tr2,i,i*b[i]);
while(m--){
char c;
scanf(" %c",&c);
if(c=='Q'){
int l,r;
scanf("%d%d",&l,&r);
printf("%lld\n",premix_sum(l,r));
}else {
int l,r,x;
scanf("%d%d%d",&l,&r,&x);
b[l]+=x;
b[r+1]-=x;
add(tr1,l,(ll)x);
add(tr1,r+1,(ll)-x);
add(tr2,l,(ll)l*x);
add(tr2,r+1,(ll)-(r+1)*x);
}
}
return 0;
}
题后反思:
树状数组只是处理数据的一种形式,只要需要实现动态的前缀和就可以使用树状数组。