根据树状数组的“区间修改,单点查询”我们知道用差分思想维护的树状数组 b , b[i] 的前缀和就是原数组 a[x] 增加的值,那么序列 a 的前缀和 a[1~x] 整体增加的值就是区间[1~x]的增加的值
原理不写了,直接写方法
在本题中,我们可以增加一个树状数组,用于维护 i*b[i] 的 前缀和。
我们建立两个数组 c[0][N] c[1][N], 即 c0, c1,起初赋值为零,c0 为 b[i] 前缀和
对于每次的add操作"C l r d",我们可分为下面4步:
- 对 c0 进行 l 上加上 d
- 对 c0 进行 r+1 上减去 d
- 对 c1 进行 l 上加上 d*l
- 对 c1 进行 r+1 上减去 d*(r+1)
我们还要建立数组sum存放a的前缀和
对于指令"Q l r"
有ans = (sum[r]+(r+1) * ask(c0 ,r)-ask(c1 ,r))-(sum[l-1]+l * ask(c0 ,l-1)-ask(c1 ,l-1))
AC代码:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
ll n,c[2][200010],a[200010],sum[200010];
void add(ll k,ll x,ll y)
{
for(;x<=n;x+=x&-x)
c[k][x]+=y;
}
ll ask(ll k,ll x)
{
ll ans=0;
for(;x;x-=x&-x)
ans+=c[k][x];
return ans;
}
int main()
{
ll m;
cin>>n>>m;
for(ll i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
sum[i]=sum[i-1]+a[i];
}
while(m--)
{
char ch;
char s[2];
scanf("%s",s);
if(s[0]=='C')
{
ll l,r,d;
scanf("%lld%lld%lld",&l,&r,&d);
add(0,l,d);
add(0,r+1,-d);
add(1,l,l*d);
add(1,r+1,-(r+1)*d);
}
else
{
ll l,r;
scanf("%lld%lld",&l,&r);
ll ans=sum[r]+(r+1)*ask(0,r)-ask(1,r);
ans-=sum[l-1]+l*ask(0,l-1)-ask(1,l-1);
printf("%lld
",ans);
}
}
return 0;
}