基本思路
基本的二维前缀和,把从(0,0)到任何一个点围成的矩形中所有的价值计算出来,然后再通过容斥原理算出所有可能正方形中的价值之和。
如果把初始的地图存入A数组,那么它的二位前缀和S就是:
$S[i][j]=\sum_{x=1}^{i} \sum_{y=1}^{j} A[x][y] $
根据容斥原理,有:
S[i][j]=s[i−1][j]+s[i][j−1]+A[i][j]−S[i−1][j−1]
这样,就可以利用上述递推式推出S数组。
同时,对于任意一个边长为R,最靠近原点的顶点为(x,y)的正方形,其内部所有点的价值之和为:
S[x+R][y+R]−S[x+R][y]−S[x][y+R]+S[x][y]
这样,就可以以O(n2)的时间复杂度解题。
我踩过的坑
-
这题有125MB的空间限制,根据上述递推式,只需要一个数组就可以完成,开两个范围为5000的二位数组貌似过不了。。
-
数据规模约定中,n最大可以到104,然而并没有那么大的地图,相当于“全图轰炸”,此时效果与n=5∗103效果相同,可以直接替换。数组开到104的话会爆空间的。
-
数组稍微开大一点点。
-
最重要的一点,数据要从(1,1)开始存,也就是说,边界应该为0,这样才能保证边界的数可以被正确计算
我一开始没有注意第3点,之后在洛谷得了90分,被这样一个数据卡住了:
可以看到,全是第0列的点。。
之后自己造了一个数据,成功发现了这个问题:
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| #include <iostream> #include <cstdio> #include <cstdlib> using namespace std;
const int MAX=5002; int s[MAX+5][MAX+5], n, r, summ, maxx, x, y, w; int xmax, ymax;
int main(){
scanf("%d %d", &n, &r); if(r>5000) r=5000; for(int i=0; i<n; i++){ scanf("%d %d %d", &x, &y, &w);
s[x+1][y+1] += w; }
for(int i=1; i<=MAX+2; i++){ for(int j=1; j<=MAX+2; j++){ s[i][j] += s[i-1][j]+s[i][j-1]-s[i-1][j-1]; } } for(int i=0; i<=MAX-r; i++){ for(int j=0; j<=MAX-r; j++){ summ = s[i+r][j+r]-s[i+r][j]-s[i][j+r]+s[i][j]; maxx = maxx>summ?maxx:summ; } } maxx = maxx>s[r][r]?maxx:s[r][r]; printf("%d", maxx);
return 0; }
|