目录
2.2.2给以(x1,y1)为左上角,(x2,y2)为右下角的子矩阵中的所有a[i,j]加上C
1.前缀和
1.1一维前缀和
1.1.1什么是一维前缀和:
前缀和是指某序列的前n项和,可以把它理解为数学上的数列的前n项和
1.1.2 一维前缀和代码:
S[i] = a[1] + a[2] + ... a[i]
a[l] + ... + a[r] = S[r] - S[l - 1]
s[i] = s[i-1] + a[i]
1.1.3代码推导与构造:
(1)推导 :
s[l-1] = a[1] + a[2] + ....+ a[l-1] ,s[r] = a[1] + a[2] + ....+ a[l-1] + a[l] + ... + a[r] (r>l)
s[r] - s[l-1] = a[l] + .....+ a[r]
s[i] = s[i-1] + a[i]
(2)普通构造:
b1 = a1 ,b2 = a2-a1 ,b3 = a3 - a2........,bn = an - an-1;
an = b1 + b2 + b3 + .......+bn
(3)代码实现:
序列a,s初始化都为0,输入a序列,然后s序列的每一项相当于前一项加上对应的a项如下:
for(int i=1;i<=n;i++)s[i] = s[i-1] + a[i];
1.1.4例题
输入一个长度为 n 的整数序列。
接下来再输入 m 个询问,每个询问输入一对 l,r。
对于每个询问,输出原序列中从第 l 个数到第 r 个数的和。
输入格式
第一行包含两个整数 n和 m。
第二行包含 n 个整数,表示整数数列。
接下来 m行,每行包含两个整数 l和 r,表示一个询问的区间范围。
输出格式
共 m 行,每行输出一个询问的结果。
数据范围
1≤l≤r≤n1
1≤n,m≤1000001
−1000≤数列中元素的值≤1000输入样例:
5 3
2 1 3 6 4
1 2
1 3
2 4输出样例:
3
6
10
1.1.5例题代码
#include<iostream>
using namespace std;
const int N = 100010;
int a[N],s[N];
int n,m;
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=n;i++)s[i] = s[i-1]+a[i];
while(m--)
{
int l,r;
cin>>l>>r;
cout<<s[r] - s[l-1]<<endl;
}
return 0;
}
1.2二维前缀和
1.2.1什么是二维前缀和
二维前缀和也称子矩阵和。子矩阵也叫”抽取矩阵”,是由原矩阵的部分行列元素所构成的矩阵,如图带有颜色部分的是一个子矩阵,里面元素之和即是子矩阵和。
1.2.2子矩阵求和思想
S[i, j] = 第i行j列格子左上部分所有元素的和
以(x1, y1)为左上角,(x2, y2)为右下角的子矩阵的和为:
S[x2, y2] - S[x1 - 1, y2] - S[x2, y1 - 1] + S[x1 - 1, y1 - 1]
1.2.3计算公式
(1)计算S[i,j]
S[i,j] = S[i-1,j] + S[i,j-1] + S[i-1,j-1] + a[i,j]
(2)计算(x1,y1),(x2,y2)之间的子矩阵所有数的和
S[x2,y2] - S[x1-1,y2] - S[x2,y1-1] + S[x1-1,y1-1]
1.2.4图片演示
假设求(3,2)为左上角,(5,3)为右下角的子矩阵和为:
S[5,3] - S[2,3] - S[5,1] + S[2,1]
图片演示(2)的由来: (1)与(2)类似得来
![](https://img-blog.csdnimg.cn/815eba050bec4a6b98e869869620b89c.png)
![](https://img-blog.csdnimg.cn/773184e1a89841788ca2f08649e54d9e.png)
![](https://img-blog.csdnimg.cn/e9ceec8539504bedb8c0ce09d7675589.png)
![](https://img-blog.csdnimg.cn/8845fd296f8e43f3a4314c821ba5013c.png)
![](https://img-blog.csdnimg.cn/7e1023c74fc24af0ad1568828f6cfd9f.png)
![](https://img-blog.csdnimg.cn/3ea8e7b8212245b3879c3b284a4c1f2d.png)
1.2.5案例
输入一个 n 行 m 列的整数矩阵,再输入 q 个询问,每个询问包含四个整数 x1,y1,x2,y2,表示一个子矩阵的左上角坐标和右下角坐标。
对于每个询问输出子矩阵中所有数的和。
输入格式
第一行包含三个整数 n,m,q。
接下来 n行,每行包含 m 个整数,表示整数矩阵。
接下来 q行,每行包含四个整数 x1,y1,x2,y2,表示一组询问。
输出格式
共 q 行,每行输出一个询问的结果。
数据范围
1≤n,m≤1000,
1≤q≤200000,
1≤x1≤x2≤n,
1≤y1≤y2≤m,
−1000≤矩阵内元素的值≤1000−1000≤矩阵内元素的值≤1000输入样例:
3 4 3
1 7 2 4
3 6 2 8
2 1 2 3
1 1 2 2
2 1 3 4
1 3 3 4输出样例:
17
27
21
1.2.6例题代码
#include<iostream>
using namespace std;
const int N =1010;
int a[N][N],s[N][N];
int n,m,q;
int main()
{
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++)
s[i][j] = s[i-1][j] + s[i][j-1] - s[i-1][j-1] + a[i][j];
while(q--)
{
int x1,y1,x2,y2;
cin>>x1>>y1>>x2>>y2;
cout<<s[x2][y2]-s[x1-1][y2]-s[x2][y1-1] + s[x1-1][y1-1]<<endl;
}
return 0;
}
2差分
2.1一维差分
2.1.1什么是一维差分
差分(difference)又名差分函数或差分运算,差分的结果反映了离散量之间的一种变化,是研究离散数学的一种工具。它将原函数f(x) 映射到f(x+a)-f(x+b) 。
2.1.2一维差分例子
常见的差分:熟悉等差数列:a1 a2 a3……an……,其中an+1= an + d( n = 1,2,…n )d为常数,称为公差, 即 d = an+1 -an , 这就是一个差分, 通常用D(an) = an+1- an来表示,于是有D(an)= d , 这是一个最简单形式的差分方程。
2.1.3 序列解释一维差分
序列:a1,a2,a3.........,an
序列: b1,b2,b3........,bn
其中:
b1 = a1
b2 = a2 - a1
b3 = a3 - a2
.......
bn = an - an-1
an = b1 + b2 + b3 +.......+bn即ai是bi的前缀和的一个项,bn是序列an的差分
2.1.4 实现代码
void insert(int l,int r,int c)
{
b[l] + = c;
b[r+1]-= c;
}
for(int i = 1;i<=n;i++)insert(i,i,a[i])//构造b[n]
![](https://img-blog.csdnimg.cn/4740deea0eaf402a969701f6da4e2bc6.png)
2.1.5例题
输入一个长度为 n 的整数序列。
接下来输入 m 个操作,每个操作包含三个整数 l,r,c表示将序列中 [l,r] 之间的每个数加上 c。
请你输出进行完所有操作后的序列。
输入格式
第一行包含两个整数 n和 m。
第二行包含 n个整数,表示整数序列。
接下来 m行,每行包含三个整数 l,r,c表示一个操作。
输出格式
共一行,包含 n个整数,表示最终序列。
数据范围
1≤n,m≤100000,
1≤l≤r≤n,
−1000≤c≤1000,
−1000≤整数序列中元素的值输入样例:
6 3
1 2 2 1 2 1
1 3 1
3 5 1
1 6 1输出样例:
3 4 5 3 4 2
2.1.6例题代码
#include<iostream>
using namespace std;
const int N = 100010;
int a[N],b[N];
int n,m;
void insert(int l,int r,int c)
{
b[l]+=c;
b[r+1]-=c;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=n;i++)insert(i,i,a[i]);
while(m--)
{
int l,r,c;
cin>>l>>r>>c;
insert(l,r,c);
}
for(int i=1;i<=n;i++)
{
b[i] += b[i-1];
cout<<b[i]<<' ';
}
return 0;
}
2.2二维差分
2.2.1什么是差分矩阵
就是在矩阵中,一行(一列)的元素与上一行(上一列)对应元素的差值,依次排列在上一行(上一列)元素对应所在位置。矩阵元素的差分分为行差分和列差分,第一行和第一列不做差分计算。
2.2.2给以(x1,y1)为左上角,(x2,y2)为右下角的子矩阵中的所有a[i,j]加上C
构造差分:
b[x1][y1]+=C
b[x2+1][y1]-=C
b[x1][y2+1]-=C
b[x2+1][y2+1] += C
计算前缀和:
b[i][j]+=b[i-1][j]+b[i][j-1]-b[i-1][j-1] ;
推导和子矩阵和相同,可看之前的图片演示
2.2.3案例
输入一个 n行 m 列的整数矩阵,再输入 q个操作,每个操作包含五个整数 x1,y1,x2,y2,c,其中 (x1,y1) 和 (x2,y2)表示一个子矩阵的左上角坐标和右下角坐标。
每个操作都要将选中的子矩阵中的每个元素的值加上 c。
请你将进行完所有操作后的矩阵输出。
输入格式
第一行包含整数 n,m,q。
接下来 n行,每行包含 m个整数,表示整数矩阵。
接下来 q行,每行包含 55 个整数 x1,y1,x2,y2,c表示一个操作。
输出格式
共 n 行,每行 m 个整数,表示所有操作进行完毕后的最终矩阵。
数据范围
1≤n,m≤1000,
1≤q≤100000,
1≤x1≤x2≤n,
1≤y1≤y2≤m,
−1000≤c≤1000,
−1000≤矩阵内元素的值≤1000输入样例:
3 4 3
1 2 2 1
3 2 2 1
1 1 1 1
1 1 2 2 1
1 3 2 3 2
3 1 3 4 1输出样例:
2 3 4 1
4 3 4 1
2 2 2 2
2.2.4例题代码
#include<iostream>
using namespace std;
const int N = 1010;
int a[N][N], b[N][N];
int n,m,q;
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()
{
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++)
{
insert(i, j, i, j, a[i][j]);
}
}
while (q--)
{
int x1, y1, x2, y2, c;
cin >> 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];
}
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
printf("%d ", b[i][j]);
}
printf("\n");
}
return 0;
}