​题目传送门​

一、坑点

  • 不能开两个数组,因为每个数组是\(5000 \times 5000\)这么大,就是\(5000 \times 5000 \times 4bytes\),\(5000 \times 5000 \times 4/1024/1024=95MB\),这题的空间是\(168MB\),所以两个数组会\(MLE\),一个数组就行了,在自己身上求前缀和。
  • \(x,y\)坐标是从\(0\)开始的,我们一般的一维前缀和、二维前缀和,下标是从\(1\)开始的。
  • 炸弹的范围会超过\(5000\),而且如果是范围是\(0\)直接返回\(0\)。

二、二维前缀和

假设在一个二维平面上,每个点具有一定的权值,我们要计算点(2,2)到(8,4)的权值和。

AcWing 99. 激光炸弹_二维

首先我们要找到这么几块面积:

AcWing 99. 激光炸弹_前缀和_02

我们可以发现,我们所要求的黄色区域,就是
黑色 - 绿色 - 粉色 + 青色 。

三、疑问解答

我们把\((xi,yi)\)作为一个格子,但是实际上他们只是一个点,所以说我们不妨认为这个点就是这个格子的中心,既然如此的话我们就可以认为是\((x_i+0.5,y_i+0.5)\)

四、实现代码

#include <bits/stdc++.h>
using namespace std;

const int N = 5010, M = 5001;
//前缀和数组
int s[N][N];

int main() {
// n:点的数量,R:边长
int n, R;
scanf("%d%d", &n, &R);

//这里本题数据有些坑,R有时输入会比我们整个矩阵大,所以这里设置输入的R过大,就直接取M
R = min(R, M); // R太大,对于我们来说没有意义,因为它>5001时,就把所有位置全部摧毁掉~

for (int i = 0; i < n; i++) {
int x, y, w;
scanf("%d%d%d", &x, &y, &w);
//因为每题的(x,y)是默认下标从0开始,与前缀和的习惯不太相符,所以,这里直接将原坐标x+1,y+1,映射到下标从(1,1)开始
x++, y++; // 0≤Xi,Yi≤5000
s[x][y] += w; //不同目标可能在同一位置
}

//二维前缀和公式
for (int i = 1; i <= M; i++)
for (int j = 1; j <= M; j++)
s[i][j] += s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];

//枚举所有边长为R的正方形,取最大值就OK了(枚举的是右下角) => (R-1)*(R-1)的矩阵
int res = 0;
for (int i = R; i <= M; i++)
for (int j = R; j <= M; j++) //二维前缀和应用
res = max(res, s[i][j] - s[i - R][j] - s[i][j - R] + s[i - R][j - R]);

//输出结果
printf("%d\n", res);
return 0;
}