解题思路
考虑用 差分数组,将
[
x
,
y
]
[x,y]
[x,y]每一个数都加k,设在
i
i
i位置上的差分数组是
d
i
d_i
di,将
d
x
+
=
k
,
d
y
+
1
−
=
k
d_x+=k,d_{y+1}-=k
dx+=k,dy+1−=k。那么此时
a
i
a_i
ai就是
[
1
,
i
]
[1,i]
[1,i]之间数的累加和,考虑维护前缀和,则
∑
i
=
1
n
a
[
i
]
\sum_{i=1}^na[i]
∑i=1na[i] =
∑
i
=
1
n
\sum_{i=1}^n
∑i=1n
∑
j
=
1
i
d
[
i
]
\sum_{j=1}^id[i]
∑j=1id[i]
我们会发现,其中
d
1
d_1
d1出现了
n
n
n次,
d
2
d_2
d2出现了
n
−
1
n-1
n−1次,
d
i
d_i
di出现了
n
−
i
+
1
n-i+1
n−i+1次,所以有
∑
i
=
1
n
a
[
i
]
\sum_{i=1}^na[i]
∑i=1na[i] =
∑
i
=
1
n
(
d
i
∗
(
n
+
1
)
−
d
i
∗
i
)
\sum_{i=1}^n(d_i*(n+1)-d_i*i)
∑i=1n(di∗(n+1)−di∗i)
树状数组要维护两种数:一种是 d i d_i di,另一种是 d i ∗ i d_i*i di∗i
代码
#include<iostream>
#include<cstdio>
#include<iomanip>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
int n,m,x,y,k,z;
long long tree[1000100],tree2[1000100],a[1000100];
void add(int x,long long y){
for(int i=x;i<=n;i+=i&(-i))
{
tree[i]+=y;
tree2[i]+=y*x;
}
}
long long sum(int x){
long long ans=0;
for(int i=x;i>0;i-=i&(-i))
ans+=tree[i]*(x+1)-tree2[i];
return ans;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
add(i,a[i]);
add(i+1,-a[i]);
}
for(int i=1;i<=m;i++)
{
scanf("%d",&k);
if(k==1)
{
scanf("%d%d%d",&x,&y,&z);
add(x,z);
add(y+1,-z);
}
if(k==2)
{
scanf("%d%d",&x,&y);
printf("%lld\n",sum(y)-sum(x-1));
}
}
}