前缀和
通过预处理,可以在O(1)的时间复杂度内询问特定区间内所有元素的和
一维前缀和
原数组:
a
[
1
]
a[1]
a[1],
a
[
2
]
a[2]
a[2],
a
[
3
]
a[3]
a[3],
a
[
4
]
a[4]
a[4]···
前缀数组:
s
[
1
]
s[1]
s[1],
s
[
2
]
s[2]
s[2],
s
[
3
]
s[3]
s[3],
s
[
4
]
s[4]
s[4]···
s
[
1
]
=
a
[
1
]
s[1]=a[1]
s[1]=a[1]
s
[
2
]
=
a
[
1
]
+
a
[
2
]
s[2]=a[1]+a[2]
s[2]=a[1]+a[2]
s
[
3
]
=
a
[
1
]
+
a
[
2
]
+
a
[
3
]
s[3]=a[1]+a[2]+a[3]
s[3]=a[1]+a[2]+a[3]
⋅
⋅
⋅
⋅
⋅
⋅
······
⋅⋅⋅⋅⋅⋅
s
[
i
]
=
a
[
1
]
+
a
[
2
]
+
a
[
3
]
+
⋅
⋅
⋅
+
a
[
i
]
s[i]=a[1]+a[2]+a[3]+···+a[i]
s[i]=a[1]+a[2]+a[3]+⋅⋅⋅+a[i]
区间
[
l
,
r
]
[l,r]
[l,r]内所有元素的和
s
u
m
=
s
[
r
]
−
s
[
l
−
1
]
sum=s[r]-s[l-1]
sum=s[r]−s[l−1]
可以只用一个数组,边输入,边得到前缀和数组,模板如下:
for(int i = 1; i <= n; ++i)
{
scanf("%d", &arr[i]);
arr[i]+=arr[i-1];
}
模板题:AcWing 795.前缀和
一道比较难的有关前缀和的题:AcWing 105.七夕祭
二维前缀和
穷学生没
i
p
a
d
ipad
ipad,借鉴一下大佬画的图
二维前缀和预处理公式:
s
[
i
,
j
]
=
s
[
i
,
j
−
1
]
+
s
[
i
−
1
,
j
]
−
s
[
i
−
1
,
j
−
1
]
+
a
r
r
[
i
,
j
]
s[i,j]=s[i,j-1]+s[i-1,j]-s[i-1,j-1]+arr[i,j]
s[i,j]=s[i,j−1]+s[i−1,j]−s[i−1,j−1]+arr[i,j],询问
(
1
,
1
)
(1,1)
(1,1)到
(
i
,
j
)
(i,j)
(i,j)这个矩形内的所有元素之和直接就是
s
[
i
,
j
]
s[i,j]
s[i,j]
查询某个小矩形内的和(再借下大佬的图)
矩形
(
x
1
,
y
1
)
(x1,y1)
(x1,y1)到
(
x
2
,
y
2
)
(x2,y2)
(x2,y2)内所有元素的和为
s
u
m
=
s
[
x
2
,
y
2
]
−
s
[
x
2
,
y
1
−
1
]
−
s
[
x
1
−
1
,
y
2
]
+
s
[
x
1
−
1
,
y
1
−
1
]
sum=s[x_2,y_2]-s[x_2,y_1-1]-s[x_1-1,y_2]+s[x_1-1,y_1-1]
sum=s[x2,y2]−s[x2,y1−1]−s[x1−1,y2]+s[x1−1,y1−1]
二维前缀和模板题链接:
AcWing 796.子矩阵的和(
P
S
PS
PS:这题还可以用压缩矩阵的方法来做)
差分
差分是前缀和的逆运算,有了差分数组,差分数组最方便的就是可以在 O ( 1 ) O(1) O(1)下进行区间修改,然后可以在 O ( n ) O(n) O(n)下查询到原数组经过修改后的数组
一维差分
原数组:
a
[
1
]
a[1]
a[1],
a
[
2
]
a[2]
a[2],
a
[
3
]
a[3]
a[3],
a
[
4
]
a[4]
a[4]···
差分数组:
s
[
1
]
s[1]
s[1],
s
[
2
]
s[2]
s[2],
s
[
3
]
s[3]
s[3],
s
[
4
]
s[4]
s[4]···
s
[
1
]
=
a
[
1
]
−
a
[
0
]
s[1]=a[1]-a[0]
s[1]=a[1]−a[0]
s
[
2
]
=
a
[
2
]
−
a
[
1
]
s[2]=a[2]-a[1]
s[2]=a[2]−a[1]
s
[
3
]
=
a
[
3
]
−
a
[
2
]
s[3]=a[3]-a[2]
s[3]=a[3]−a[2]
⋅
⋅
⋅
⋅
⋅
⋅
······
⋅⋅⋅⋅⋅⋅
s
[
i
]
=
a
[
i
]
−
a
[
i
−
1
]
s[i]=a[i]-a[i-1]
s[i]=a[i]−a[i−1]
原数组就是差分数组的前缀和数组
a
[
1
]
=
s
[
1
]
a[1]=s[1]
a[1]=s[1]
a
[
2
]
=
s
[
1
]
+
s
[
2
]
a[2]=s[1]+s[2]
a[2]=s[1]+s[2]
a
[
3
]
=
s
[
1
]
+
s
[
2
]
+
s
[
3
]
a[3]=s[1]+s[2]+s[3]
a[3]=s[1]+s[2]+s[3]
⋅
⋅
⋅
⋅
⋅
⋅
······
⋅⋅⋅⋅⋅⋅
a
[
i
]
=
s
[
1
]
+
s
[
2
]
+
s
[
3
]
+
⋅
⋅
⋅
+
s
[
i
]
a[i]=s[1]+s[2]+s[3]+···+s[i]
a[i]=s[1]+s[2]+s[3]+⋅⋅⋅+s[i]
把 [ l , r ] [l,r] [l,r]区间内的每一个数都加上一个常数c,那么对差分数组的处理是 s [ l ] + = c , s [ r + 1 ] − = c s[l]+=c,s[r+1]-=c s[l]+=c,s[r+1]−=c,差分数组需要另开一个数组,而前缀和不用
模板题:AcWing 797.差分
二维差分
再用一下大佬画的好图
如何构造二维差分矩阵?
设 b b b为 a a a的差分矩阵,那么 a [ i ] [ j ] a[i][j] a[i][j]这个元素的值就是 b b b矩阵中 ( 1 , 1 ) (1,1) (1,1)到 ( i , j ) (i,j) (i,j)矩形内所有元素的和。
如果在
b
b
b矩阵中把
b
[
n
]
[
m
]
b[n][m]
b[n][m]这个元素加上常数
c
c
c,那么在
a
a
a中,对所有的
i
(
i
>
=
n
)
,
j
(
j
>
=
m
)
i(i>=n),j(j>=m)
i(i>=n),j(j>=m),
a
[
i
]
[
j
]
a[i][j]
a[i][j]都会加上
c
c
c。
首先先说一下如何把
(
x
1
,
y
1
)
(x_1,y_1)
(x1,y1)到
(
x
2
,
y
2
)
(x_2,y_2)
(x2,y2)的矩阵每个元素加一个常数
c
c
c,对
b
b
b矩阵有
b
[
x
1
]
[
y
1
]
+
=
c
,
b[x_1][y_1]+=c,
b[x1][y1]+=c,
b
[
x
2
+
1
]
[
y
1
]
−
=
c
b[x_2+1][y1]-=c
b[x2+1][y1]−=c
b
[
x
1
]
[
y
2
+
1
]
−
=
c
b[x_1][y_2+1]-=c
b[x1][y2+1]−=c
b
[
x
2
+
1
]
[
y
2
+
1
]
+
=
c
b[x_2+1][y_2+1]+=c
b[x2+1][y2+1]+=c
首先把数组定义为全局变量,全局变量里面元素所有为0,零矩阵肯定是零矩阵的差分矩阵。那么我们在每输入一个原矩阵的值时就相当于在零矩阵中插入这个数
,那么我们就可以同时构造出原矩阵的差分矩阵。那么由差分矩阵求原矩阵的时候就可以由二维前缀和来求
模板题如下:AcWing 798.差分矩阵
AC代码如下:
#include <iostream>
using namespace std;
const int MAXN=1010;
int a[MAXN][MAXN],b[MAXN][MAXN];
int n,m,q,x1,y1,x2,y2,c;
void insert(int x1,int y1,int x2,int y2,int c)
{
b[x1][y1]+=c;
b[x2+1][y1]-=c;
b[x1][y2+1]-=c;
b[x2+1][y2+1]+=c;
}
int main()
{
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
{
scanf("%d",&a[i][j]);
insert(i,j,i,j,a[i][j]);
}
while(q--)
{
scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&c);
insert(x1,y1,x2,y2,c);
}
for(int i=1;i<=n;++i)
{
for(int j=1;j<=m;++j)
{
b[i][j]=b[i-1][j]+b[i][j-1]-b[i-1][j-1]+b[i][j];
printf("%d ",b[i][j]);
}
printf("\n");
}
return 0;
}