[HNOI2003]激光炸弹

[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];
}

posted @ 2019-07-18 09:55  a1b3c7d9  阅读(433)  评论(0编辑  收藏  举报