书接上回
我们之前已经学习了一维的前缀和与差分
现在,让我们拓展一个维度,来到二维。
相信有了前面的基础
学起这些来一定不会很难
二维前缀和
概念
让我们先温习一下一维前缀和的内容
假设: b [ i ] = a [ 1 ] + a [ 2 ] + a [ 3 ] + . . . . . . a [ i ] b[i]=a[1]+a[2]+a[3]+......a[i] b[i]=a[1]+a[2]+a[3]+......a[i]
则a[i]至a[j]的区间和为 b [ j ] − b [ i ] b[j]-b[i] b[j]−b[i]
而二维前缀和自然就是要拓展到平面了 如图
便是一整个平面的数据总和
(其实
s
u
m
[
i
]
[
j
−
1
]
sum[i][j-1]
sum[i][j−1]和
s
u
m
[
i
−
1
]
[
j
]
sum[i-1][j]
sum[i−1][j]里面都包含着
s
u
m
[
i
−
1
]
[
j
−
1
]
sum[i-1][j-1]
sum[i−1][j−1])
我们稍微改一下上面的图片:
其实紫色区域(看得到的)就是
a
[
i
]
[
j
]
a[i][j]
a[i][j]
稍微用一下容斥原理:
我们可以得到:
s
u
m
[
i
]
[
j
]
=
s
u
m
[
i
]
[
j
−
1
]
(
蓝
加
黄
)
+
s
u
m
[
i
−
1
]
[
j
]
(
红
加
黄
)
−
s
u
m
[
i
−
1
]
[
j
−
1
]
+
a
[
i
]
[
j
]
sum[i][j]=sum[i][j-1](蓝加黄)+sum[i-1][j](红加黄)-sum[i-1][j-1]+a[i][j]
sum[i][j]=sum[i][j−1](蓝加黄)+sum[i−1][j](红加黄)−sum[i−1][j−1]+a[i][j]
这样对于二维区间和的查询,也可以维持在
O
(
1
)
O(1)
O(1)
例题
二维前缀和
题目描述
给出一 m m m行 n n n列的矩阵, q q q次询问左上角为 ( x 1 , y 1 ) (x1,y1) (x1,y1),右下角为 ( x 2 , y 2 ) (x2,y2) (x2,y2)的子矩阵中数的和
输入格式
第一行三个整数 n , m , q n,m,q n,m,q
之后 行每行 个数,代表给出的矩阵
之后 行每行四个整数 表示一次询问
输出格式
共 行,每行一个整数表示答案
样例
输入样例
3 3 2
0 0 1
1 1 0
1 0 1
1 1 3 3
2 1 2 3
输出样例
5
2
代码
#include<bits/stdc++.h>
using namespace std;
int a[1005][1005];
long long sum[1005][1005],ms;
int main(){
ios::sync_with_stdio(false);//加快cin读入(当然你用scanf可以不用管这些)
int n,m,q;
cin>>n>>m>>q;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>a[i][j];
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j];//前缀和计算过程
}
}
for(int i=1;i<=q;i++){
int a1,b1,a2,b2;
cin>>a1>>b1>>a2>>b2;
cout<<sum[a1-1][b1-1]+sum[a2][b2]-sum[a1-1][b2]-sum[a2][b1-1]<<endl;//区间查询
}
}
二维差分
概念
还是那句话:
差分是前缀和的逆运算
这句话放到二维里肯定也是适用的
a
[
i
]
[
j
]
a[i][j]
a[i][j]为
b
[
i
]
[
j
]
b[i][j]
b[i][j]的二维前缀和
因为二维差分跟二维有关系,所以区间修改的是一整个平面的值
这里画个图示意一下
图1:
我们修改的是
b
b
b数组的黄色区域,但是
a
a
a数组整个蓝色区域也跟着黄色做了同样的修改(想一想,为什么)
要做到区间修改还需要这样
图2 | 图3 | 图4 |
---|
当黄色区域改变后,所有非灰色区域会随着黄色区域的改变而改变
再小小的用一下容斥
设图
n
n
n随黄改变的地方为
S
n
S_n
Sn
显然
红圈内部分为
S
1
−
S
2
−
S
3
+
S
4
S_1-S_2-S_3+S_4
S1−S2−S3+S4
所以修改区间的操作只需要加加减减就可以了
代码实现
对于这个更方便的操作是
i
n
s
e
r
t
insert
insert(插入)
具体代码如下
int a[10005][10005],b[10005][10005];
void insert(int a1,int a2,int b1,int b2,int c){\\既可以构造也可以修改,2合1
b[a1][b1]+=c;
b[a1][b2+1]-=c;
b[a2+1][b1]-=c;
b[a2+1][b2+1]+=c;
}
后面再计算
b
b
b的二维前缀和就行了
自此前缀和与差分的内容已经讲完了,更多的拓展知识也应该建立在这些基础之上。
如果还感兴趣,就自己去探索吧!