[HNOI2003]激光炸弹
给出一个平面直角坐标系,并给出其中n个整点,第i个点为\((x_i,y_i)[x_i,y_i\in[0,5000]]\),第i个点上标记一个数字\(v_i\),现在请用一个\(R\times R\)的正方形,在其边界与x轴y轴对齐的同时,使覆盖的点上的标记的数字之和最大\(R>0\)。
解
可以转化为网格图问题,考虑方向,行列,对角线,矩形,按照枚举的思想,我们可以枚举正方向的右下角的顶点,时间复杂度是可以支持的,现在问题就变成如何快速查询一个正方形区域内的数字之和,显然维护一个二位数组前缀和,再配上查询操作即可,注意传统的方法不允许坐标为0,于是不妨把所有的坐标全部+1。
参考代码:
#include <iostream>
#include <cstdio>
#define il inline
#define ri register
#define ll long long
using namespace std;
int M[5050][5050];
il int ask(int,int,int,int);
template<class free>
il free Max(free,free);
int main(){
int n,r,ans(0);scanf("%d%d",&n,&r);
if(!r)return putchar(48),0;
for(int i(1),y,x,v;i<=n;++i)
scanf("%d%d%d",&y,&x,&v),M[y+1][x+1]=v;
for(int i(1),j;i<=5001;++i)
for(j=1;j<=5001;++j)
M[i][j]+=M[i-1][j]+M[i][j-1]-M[i-1][j-1];
for(int i(1),j,y,x;i<=5001;++i)
for(j=1;j<=5001;++j){
y=i+r-1,x=j+r-1;
if(y>5001)y=5001;if(x>5001)x=5001;
ans=Max(ans,ask(y,x,i,j));
}
printf("%d",ans);
return 0;
}
template<class free>
il free Max(free a,free b){
return a>b?a:b;
}
il int ask(int y1,int x1,int y2,int x2){
return M[y1][x1]-M[y1][x2-1]-M[y2-1][x1]+M[y2-1][x2-1];
}