小问题
有一个长度为 N N N 的序列 A A A,求 A A A 的指定区间内的和。
前缀和
首先,我们将
s
u
m
x
sum_x
sumx 表示为
∑
i
=
1
x
a
i
\sum_{i=1}^xa_i
∑i=1xai,可知
s
u
m
sum
sum 序列是可以
O
(
N
)
O(N)
O(N) 求的。
接下来,对于每组
l
,
r
l,r
l,r,显然
∑
i
=
l
r
a
i
=
∑
i
=
1
r
a
i
−
∑
i
=
1
l
−
1
a
i
=
s
u
m
r
−
s
u
m
l
−
1
\sum_{i=l}^ra_i=\sum_{i=1}^ra_i-\sum_{i=1}^{l-1}a_i=sum_r-sum_{l-1}
∑i=lrai=∑i=1rai−∑i=1l−1ai=sumr−suml−1。
妈呀这不是小学生容斥吗???
二维前缀和
顾名思义,二维前缀和就是求子矩阵元素和。
同理,我们将
s
u
m
x
,
y
sum_{x,y}
sumx,y 表示为
∑
i
=
1
x
∑
j
=
1
y
a
i
,
j
\sum_{i=1}^x\sum_{j=1}^ya_{i,j}
∑i=1x∑j=1yai,j,又可知
s
u
m
sum
sum 序列是可以
O
(
N
2
)
O(N^2)
O(N2) 求的。
什么,你说不会。好吧,可知
s
u
m
i
,
j
=
a
i
,
j
+
s
u
m
i
−
1
,
j
+
s
u
m
i
,
j
−
1
−
s
u
m
i
−
1
,
j
−
1
sum_{i,j}=a_{i,j}+sum_{i-1,j}+sum_{i,j-1}-sum_{i-1,j-1}
sumi,j=ai,j+sumi−1,j+sumi,j−1−sumi−1,j−1。
妈呀这不又又是小学生容斥吗???
和预处理一样,对于每组
x
1
,
y
1
,
x
2
,
y
2
x1,y1,x2,y2
x1,y1,x2,y2,显然
∑
i
=
x
1
x
2
∑
j
=
y
1
y
2
a
i
,
j
=
s
u
m
x
2
,
y
2
−
s
u
m
x
2
,
y
1
−
1
−
s
u
m
x
1
−
1
,
y
2
+
s
u
m
x
1
−
1
,
y
1
−
1
\sum_{i=x1}^{x2}\sum_{j=y1}^{y2}a_{i,j}=sum_{x2,y2}-sum_{x2,y1-1}-sum_{x1-1,y2}+sum_{x1-1,y1-1}
∑i=x1x2∑j=y1y2ai,j=sumx2,y2−sumx2,y1−1−sumx1−1,y2+sumx1−1,y1−1。
妈呀这不又又又又是小学生容斥吗???
差分
我们将
c
i
c_i
ci 记为
a
i
+
1
−
a
i
a_{i+1}-a_i
ai+1−ai,会发现一个好玩的性质:
∑
i
=
0
x
−
1
c
i
=
a
x
\sum_{i=0}^{x-1}c_i=a_x
∑i=0x−1ci=ax
证:
c
0
+
c
1
+
c
2
+
.
.
.
+
c
x
−
1
=
a
1
+
(
a
2
−
a
1
)
+
(
a
3
−
a
2
)
+
.
.
.
+
(
a
x
−
a
x
−
1
)
=
a
x
c_0+c_1+c_2+...+c_{x-1}\\ =a_1+(a_2-a_1)+(a_3-a_2)+...+(a_x-a_{x-1})\\ =a_x
c0+c1+c2+...+cx−1=a1+(a2−a1)+(a3−a2)+...+(ax−ax−1)=ax
差分这个方法在区间减法很有用。把
l
l
l 至
r
r
r 这个区间的数同时减
x
x
x,那么 c[l-1]-=x,c[r]+=x
你在期待代码吗,好的。
//前缀和
for(int i=1;i<=n;i++) sum[i]=sum[i-1]+a[i];
int get(int l,int r){
return sum[r]-sum[l-1];
}
//二维前缀和
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
sum[i][j]=a[i][j]+sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];
}
}
int get(int x,int y,int xx,int yy){
return sum[xx][yy]-sum[x-1][yy]-sum[xx][y-1]+sum[x-1][y-1];
}
//差分
for(int i=1;i<=n;i++){
c[i]=a[i]-a[i-1];
}
void update(int l,int r,int x){
c[l-1]-=x,c[r]+=x;
}