赞
踩
题目描述:
输入一个n行m列的整数矩阵,再输入q个操作,每个操作包含五个整数x1, y1, x2, y2, c,其中(x1, y1)和(x2, y2)表示一个子矩阵的左上角坐标和右下角坐标。
每个操作都要将选中的子矩阵中的每个元素的值加上c。
请你将进行完所有操作后的矩阵输出。
输入格式
第一行包含整数n,m,q。
接下来n行,每行包含m个整数,表示整数矩阵。
接下来q行,每行包含5个整数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
分析:
本题考察二维差分,差分的具体概念见AcWing 100 IncDec序列。鉴于前缀和以及一维差分较为简单,不再单独写题解,简单的总结下:
一维前缀和:设有数组a[n],s[n]为a的前缀和数组,有s[i] = a[1] + a[2] +... + a[i],有了前缀和,我们可以在O(1)的时间内求出任意一段区间内a数组的和,比如a[l] + a[l + 1] + ... +a[r] = s[r] - s[l - 1].
二维前缀和:设有二维数组a[m][n],s[i][j]为a[i][j]及其之前所有元素的前缀和,比如下图中,从a[1][1]到a[i][j]矩形所有元素的和为s[i][j],s[i - 1][j]表示直线b上边所有元素的和,s[i][j - 1]表示直线a左边所有元素之和,则s[i][j] = s[i - 1][j] +s[i][j - 1] - s[i - 1][j -1] + a[i][j],因为a的左边与b的上边元素求和时对该区域内元素求了两遍和,所以需要减去s[i - 1][j - 1].
既然知道了求二维数组的前缀和,那么求以(x1,y1)为矩形左上角,(x2,y2)为矩形右上角的矩形内所有元素的和可以用公式s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1]来计算。如下图所示,(x2,y2)的前缀和减去1左边的元素之和(s[x1 - 1][y2])再减去2上边的元素之和(s[x2][y1 - 1])再加上多减去的3区域(s[x1 - 1][y1 - 1]。
一维差分:差分是与前缀和相对的概念,设有数组a[n],b[n],b[n]的前缀和数组为a[n],则b[n]为a[n]的差分数组,即b[1] = a[1],b[2] = a[2] - a[1],b[3] = a[3] - a[2],...,b[n] = a[n] - a[n - 1].可见把a数组相邻两个元素相见即可得到b数组,对b数组求和即可得到a,a[n] = a[1] + a[2] - a[1] + a[3] - a[2] +... + a[n] - a[n - 1] = b[1] + b[2] + ... + b[n]。差分的重要性质就是已知a数组可以求出b数组,已知b数组也可以反推出a数组。一维前缀和使得我们在O(1)的时间内求出区间l到r范围内元素的和,但是一旦涉及区间更新前缀和数组便没那么简单了,比如对下标1 - 3范围内的元素都加上2,2 - 4范围内的元素都加上1,每次进行区间更新,区间中a的每个元素都进行了更新,前缀和自然也一直在变。假设有m次区间更新操作,每次对长度为n的区间范围内的元素都加上一个数c,一共需要进行m * n次操作才能得到最终的a数组。差分则是简化此操作的利器,比如数组a,下标1 - 10的元素分别为1 2 3 4 5 6 7 8 9 2,对下标为2 - 9的元素都加上3需要进行8次操作,而该操作对a的差分数组的影响仅仅是b[2] += 3,b[10] -= 3。对区间的操作简化为了对区间端点的操作,不管区间更新多长,对b数组的影响始终就两个元素,m次更新结束,对b数组一共进行了2m次操作,再利用b求出a问题即可解决。总之,给数组a[l,r]范围内的数都加上c的操作等价于b[l] += c,b[r + 1] -= c的操作。
二维差分:经过了这么多的铺垫,终于讲到了本题的主题二维差分。设有二维数组a[m][n]及它的差分数组b[m][n],我们唯一知道的一个性质就是b的前缀和是a,由二维前缀和的一个公式s[i][j] = s[i - 1][j] +s[i][j - 1] - s[i - 1][j -1] + a[i][j]可得,a[i][j] = a[i - 1][j] + a[i][j - 1] - a[i - 1][j - 1] + b[i][j],变形得b[i][j] = a[i][j] + a[i - 1][j - 1] - a[i - 1][j] - a[i][j - 1]。等式的右边可以理解为一个长度为1的单位矩阵,b[i][j]的值就等于以a[i][j]为右下顶点的正方形的正对角线上两个端点的和减去副对角线上两个端点的和。上面的式子对我们理解二维差分尤为重要(但是为啥很少见讲二维差分的文章提及),我们可以得出一个结论,对于二维数组a,如果a[i][j],a[i - 1][j - 1],a[i - 1][j],a[i][j - 1]都加上c,b[i][j]不变,如果只有一条边上的两个端点加上c,比如a[i][j]和a[i][j - 1],b[i][j]的值也不变。那么对于下图的矩阵,我们对以(x1,y1)为左上顶点,(x2,y2)为右下顶点的矩形区域内元素都加上c,思考下会改变哪些b数组的元素,显而易见,矩形内部的点比如a[x][y](x1<x<x2,y1<y<y2),以该点为右下顶点的边长为1的正方形区域的四个顶点元素都是加上c的,根据上面的结论b[x][y]不会改变,那么对于像下图右边界上两条黄线构成的正方形呢,由于只有一条黑色边上两个顶点的值加上了c,所以该正方形右下端点的b数组的值也不会改变。经过分析,对矩形区域内所有的元素都加上c后,b数组受影响的只有四个点,分别是b[x1][y1] ,b[x2 + 1][y1],b[x1][y2 + 1] , b[x2 + 1][y2 + 1] ;以这些点为右下端点的正方形也就是下图中红色的四个正方形,由于只有一个端点的值被改变,所以他们的b值均被改变,具体变化为b[x1][y1] += c; b[x2 + 1][y1] -= c;b[x1][y2 + 1] -= c;b[x2 + 1][y2 + 1] += c;于是我们得出的结论是对该矩形区域内所有元素都加上c的结果等价于对b中四个点的操作。
总的代码如下:
- #include <iostream>
- using namespace std;
- const int maxn = 1005;
- int a[maxn][maxn],b[maxn][maxn];
- 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(){
- int n,m,q,x1,y1,x2,y2,c;
- 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]);
- for(int i = 1;i <= n;i++)
- for(int j = 1;j <= m;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++)
- a[i][j] = a[i - 1][j] + a[i][j - 1] - a[i - 1][j - 1] + b[i][j];
- for(int i = 1;i <= n;i++){
- for(int j = 1;j <= m;j++){
- printf("%d ",a[i][j]);
- }
- printf("\n");
- }
- return 0;
- }

我们在初始化二维差分数组时,可以直接用上面推导的求b的公式,一般情况下则是直接将a中原有的元素逐个插入,以此来影响b,这样则不需要要考虑由a推出b的公式了。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。