题目描述:
在 n×n 的格子上有 m 个地毯。
给出这些地毯的信息(x1,y1,x2,y2),问每个点被多少个地毯覆盖。
1<=n<=1000,1<=m<=1e5.
思路:
如果直接开二维数组暴力模拟做的话,时间复杂度大概是O(n³)差不多是1e11的水平,100%会超时,而且这题肯定没有这么简单,所以需要考虑进行优化。再次看题,发现这题和一维数组的差分题目类似,也是先进行部分区域的增加,最后再询问整体的数值。以防有人不熟悉或者忘了差分算法的用法,我还是先解释一下。所谓差分,就是构造一个新的数组表示数组相邻元素之间的差值。这样就可以通过改变一个量来达到改变一系列数效果。然后再通过加和的方式还原数组。
/*
一维差分举例及原理:
basic [6]={ 0 , 2 , 3 , 8 , 5 , 4 }//第一个数最好是0,作为加和的基础值(不需要也可以,将差分数组第一个数作为基础值便可,但写起来比较麻烦,这里不做赘述)
那么差分数组就应该是这样:
chafen [6]={ 0 , 2 , 1 , 5 , -3 , -1 }
我们再修改其中一个值,比如将第3个加上2。这样,在最后做累加时,这个改变就会影响这个位置及以后元素的值。(原理可以从前缀和公式中看出:basic[i]=basic[i-1]+chafen[i])。
修改后basic={ 0 , 2 , 5 , 10 , 7 , 6 }//可以看到第三个元素及以后的值都 +2 了,我们不妨把这个叫做这个改变的作用域。
只对中间部分元素操作也很简单,只需相应位置差分元素再减去相应的值就行。
*/
那么我们将情况推广到二维数组,我们不妨想想,我们求二维数组前缀和的方法。实际上,求二维数组前缀和就相当于求二维数组第一个元素与该元素所组成的矩形里所有元素的和。公式为:basic [y] [x] = basic [y-1][x] +basic [y][x-1] -basic [y-1] [x-1](容斥)+chafen [y] [x] 。那么根据图和公式可以得出,这个改变的作用域就是从该元素与最后一个元素所组成的矩形。
那么同一维数组一样,我们只需要再对下图指出元素进行操作,就能起到改变部分元素的值的效果。这样时间复杂度就降到了O(n),轻松AC。
代码:
#include <iostream>
#include <stdio.h>
using namespace std;
int block[1005][1005];// 因为初始数组全部都是0,没必要定义两个数组。
int main() //只需要对一个进行操作即可。
{
int n,m,x1,y1,x2,y2;//题目中的变量
int i,j,k,t;//个人喜欢用的一套循环变量
cin>>n>>m;
while(m--)
{
cin>>x1>>y1>>x2>>y2;
block[y1][x1]++;
block[y2+1][x2+1]++;
block[y2+1][x1]--;
block[y1][x2+1]--;
}
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
{
block[j][i]=block[j-1][i]+block[j][i-1]-block[j-1][i-1]+block[j][i];
cout<<block[j][i]<<' ';
}
cout<<endl;
}
return 0;
}