树状数组的区间查询区间加
题目:
已知一个数列,你需要进行下面两种操作:
1.将某区间每一个数加上x
2.求出某区间每一个数的和
考虑在用树状数组区间查询,单点修改时我们做了什么
我们用了一个差分数组\(b[i]\),则\(\sum_{i=1}^x b[i]\)即是对\(a[x]\)的贡献
如果我们考虑\(b\)数组对\(a\)的前缀和的贡献
\(b\)对\(\sum_{i=1}^x a[i]\)的贡献
为\(\sum_{i=1}^x \sum_{j=1}^i b[j]\)
化简它
\(\sum_{i=1}^x (x+1-i) \times b[i]\)
\(=(x+1)\sum_{i=1}^x b[i]-\sum_{i=1}^x i \times b[i]\)
我们用两个树状数组分别维护这两项即可
Code:
#include <cstdio>
#define ll long long
const int N=100010;
ll c[2][N],s[N];
int n,m;
void add(int typ,int x,ll delta)
{
while(x<=n)
{
c[typ][x]+=delta;
x+=x&-x;
}
}
ll query(int typ,int x)
{
ll ans=0;
while(x)
{
ans+=c[typ][x];
x-=x&-x;
}
return ans;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%lld",s+i);
s[i]+=s[i-1];
}
int opt,x,y;ll k;
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&opt,&x,&y);
if(opt==1)
{
scanf("%lld",&k);
add(0,x,k),add(0,y+1,-k);
add(1,x,k*x),add(1,y+1,-k*(y+1));
}
else
printf("%lld\n",s[y]+(y+1)*query(0,y)-query(1,y)-(s[x-1]+x*query(0,x-1)-query(1,x-1)));
}
return 0;
}
2018.7.26