赞
踩
二维差分相对一维差分会复杂一点,而且还要结合二维前缀和的一些细节处理
在差分思想中,构造并不是那么重要,而是其中A、B数组的角色。
我们假想存在一个数组B,输入的A数组是B数组的前缀和——差分思想核心
也就是说:我们对B[i][j] 添加一个数后,从A[i][j]开始一直到A[n][n]都将 添加一个数
所以,这样就很巧妙的将 需要循环处理的,转化成了对B数组的某一个的处理(好好理解这句话!!!看懂这个你就看懂了差分的核心思想)
当然,可能还有一点比较奇怪的是:那怎么解释开始时和输入后的数组呢?
开始时,前缀和数组A(输入的)和B数组都是空的,也都是0,此时是满足前缀和概念的
然后当我们开始往A数组写入数据时,此时B数组是空的,那么此时就A数组好像就并不是B数组的前缀和了!
所以,此时我们需要向B数组对应位置也插入相应的值,只是需要处理的坐标都是
i , j
这样就满足了A是B的前缀和的概念了
因为A数组是B数组的前缀和,所以当我们对B[i][j]加上某一个常数c后,A数组从[ i, j ]
后面都加上了c
具体来说,当i = 2 , j = 1
时加上一个常数时,后面(红色框起来的)都加上了一个常数
然而题目所要求的的是,我们在[ x1 , y1 ]
到[ x2 , y2 ]
范围加上一个常数c即可,所以我们就需要像二维前缀和数组一样减去某些位置的值
假设,我们只需要计算[2 , 1]
到[3 , 3]
的数据
也就是只需要对红色框加上常数即可
当我们对a[2][1]
加上常数c后,从[2 , 1]
到[4 , 4]
都加上了c,我们需要减去对应的值即可
从图中我们不难看出,我们需要进行
a[3 , 1] -= c
a[2 , 3] -= c
但是这里我们会发现会减去两次,所以我们需要对a[3 , 3] += c
这里对应的(x1,y1)和(x2,y2)分别是(2,1)和(3,3)
所以抽象一下,我们就能得到这样一个核心插入逻辑
因为前面我们对于A数组的设计是B数组的前缀和,但是在前面的相关逻辑处理中,我们并没有体现,所以我们需要在结果计算中体现
这里就与前缀和的处理逻辑一样了
b[i][j] += b[i-1][j] + b[i][j - 1] - b[i - 1][j - 1];
假设,当前我们需要计算[2 , 2]
时,我们就需要加上左边和上边的值
但是因为左上角的那个值会被加两次,所以这里就要 加上一个c
#include<iostream> using namespace std; int n , m , q; const int N = 1008; int a[N][N] , b[N][N]; 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]); } } int x1 , y1 , x2 ,y2 , c; 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]; cout << b[i][j] << " "; } cout << endl; } return 0 ; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。