转载!写的很好:【学习总结】一、二维前缀和 && 一、二维差分_二维差分求前缀和-CSDN博客
一维前缀
数列的前n项和Sn 减去 数列的前n-1项和Sn-1等于它的通项公式,而将这种方式运用在编程中就能快速算出每一项对应的前缀和。
#include <iostream>
using namespace std;
const int N = 1e5+10;
int nums[N] , presum[N] , n , m , l , r;
int main()
{
cin>>n>>m;
for (int i = 1 ; i <= n ; ++i)
{
cin>>nums[i];
presum[i] = presum[i-1] + nums[i];
}
while(m--)
{
cin>>l>>r;
cout<<presum[r] - presum[l-1]<<endl; //这就算出来了,多爽啊~
}
return 0;
}
二维前缀
一维前缀和s[ i ] = s[ i-1 ] +a[ i ] ; 你会发现对于每一个前缀和都可以利用到它的前一个前缀和 ,然后只需要加上这一项的原数组值,二维前缀和:b[i] [j] = b[i-1][j] + b[i][j-1 ] - b[i-1][ j-1] + a[i] [j]
求子矩阵和
子矩阵和:给你二维数组中两个坐标 (x1 , y1)和 (x2 , y2),以它们作为一个子矩阵的左上角坐标和右下角坐标,求所围成的矩阵中所有元素之和。
Sum(之矩阵和) = b[x2][y2] - b[x2][y1- 1] - b[x1 - 1][y2] + b[x1 - 1][y1 - 1]
#include <iostream>
using namespace std;
const int N=1001;
int a[N][N], b[N][N], n, m, q, x1, x2, y1, y2;
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]);
b[i][j] = b[i-1][j] + b[i][j-1] - b[i-1][j-1] + a[i][j];//求二维前缀和
}
}
while(q--){
scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
printf("%d\n", b[x2][y2] - b[x1 - 1][y2] - b[x2][y1 - 1] + b[x1 - 1][y1 - 1]);
}
return 0;
}
一维差分
类似于数学中的求导和积分,差分可以看成前缀和的逆运算
nums[i] = presum[i] - presum[i-1]
#include <iostream>
using namespace std;
const int N=100010;
int presum[N], nums[N], n, m, l, r, c;
int main(){
scanf("%d%d", &n, &m);
for (int i = 1 ; i <= n ; i++){
scanf("%d", &presum[i]);
nums[i] = presum[i] - presum[i-1]; //构造一维差分
}
while (m--)
{
scanf("%d%d%d", &l, &r, &c);
nums[l] += c;
nums[r+1] -= c; //我们只想对[l , r]区间元素产生影响,需要对r ~ n 元素的影响消除掉。
}
for (int i = 1 ; i <= n ; i++){
presum[i] = presum[i-1] + nums[i]; //我们改变的是nums[i],还要采用一维前缀和求回presum[i]。
printf("%d ", presum[i]);
}
return 0;
}
二维差分
二维差分就是通过a[][] 的二维前缀和数组b[][] 去求它的二维差分数组a[][]
求二维差分公式:a[i][j] = b[i][j] - b[i-1][j] - b[i][j-1] + b[i-1][j-1];
#include <iostream>
using namespace std;
const int N = 1e3 + 10;
int b[N][N], a[N][N];
void insert(int x1, int y1, int x2, int y2, int c)
{
a[x1][y1] += c;
a[x2 + 1][y1] -= c;
a[x1][y2 + 1] -= c;
a[x2 + 1][y2 + 1] += c;
}
int main()
{
int n, m, q;
cin >> n >> m >> q;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
cin >> b[i][j],
a[i][j] = b[i][j] - b[i][j-1] - b[i-1][j] + b[i-1][j-1];//构建二维差分数组
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] + a[i][j]; //二维前缀和
}
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
printf("%d ", b[i][j]);//输出操作完所有步骤后的b[][]
}
printf("\n");
}
return 0;
}
b数组为空,那么 a数组一开始也为空,但是实际上 b数组并不为空,因此我们每次让 a数组以(i,j)为左上角到以(i,j)为右下角面积内元素(其实就是一个小方格的面积)去插入 c=b[i][j],等价于原数组b中(i,j) 到(i,j)范围内 加上了 b[i][j] (a[][]就是b[][]的二维差分数组呢),因此执行n*m次插入操作,就成功构建了差分a数组