对于区间修改、区间查询这样的简单问题,打一大堆线段树确实是不划算,所以学习下区间查询+区间修改的树状数组
设原数组是a[n],差分数组c[n],c[i]=a[i]-a[i-1],
那么明显地a[i]=sigma (c[i]),如果想要修改a[i]到a[j](比如+v),只需令c[i]+=v,c[j+1]-=v
观察式子:
a[1]+a[2]+...+a[n]
= (c[1]) + (c[1]+c[2]) + ... + (c[1]+c[2]+...+c[n])
= n*c[1] + (n-1)*c[2] +... +c[n]
= n * (c[1]+c[2]+...+c[n]) - (0*c[1]+1*c[2]+...+(n-1)*c[n]) (式子①)
我们假设 c2[i] = (i-1)*c[i]
每当修改c的时候,就同时修改一下c2,这样复杂度就不会改变
sigema(a1~an) = n * (c[1]+c[2]+...+c[n]) - (0*c[1]+1*c[2]+...+(n-1)*c[n]) = n*sigma(c[n]) - sigma(c2[n]);
练习题
#include<bits/stdc++.h>
using namespace std;
int n;
int lowbit(int x)
{
return x&(-x);
}
void update(long long c[],int x,long long num)
{
for(int i = x;i <= n;i += lowbit(i))
{
c[i] += num;
}
}
long long query(long long c[],int x)
{
long long sum = 0;
for(int i = x;i >= 1;i -= lowbit(i))
{
sum += c[i];
}
return sum;
}
long long c[200010];
long long c2[200010];
long long sum[200010];
int main()
{
int q,op,x,y;
long long num;
scanf("%d",&n);
sum[0] = 0;
memset(c2,0,sizeof(c2));
memset(c,0,sizeof(c));
for(int i = 1;i <= n;i ++)
{
scanf("%lld",&sum[i]);
update(c,i,sum[i] - sum[i - 1]);
update(c2,i,(i - 1LL)*(sum[i] - sum[i - 1]));
}
scanf("%d",&q);
while(q --)
{
scanf("%d %d %d",&op,&x,&y);
if(op == 1)
{
scanf("%lld",&num);
update(c,x,num);
update(c,y+1LL,-num);
update(c2,x,(x-1LL)*num);
update(c2,y+1LL,-y*num);
}
else
{
printf("%lld\n", (y*query(c,y) - query(c2,y) ) - ( (x-1LL) *query(c,x-1LL) -query(c2,x - 1LL) ) );
}
}
return 0;
}