HAOI 2007 修筑绿化带 题解

题目传送门

题目大意: 在一个 n × m n\times m n×m 的大矩形里面找一个 a × b a\times b a×b 的子矩形,再从子矩形里面找一个 c × d c\times d c×d 的子子矩形,要求子子矩形的边沿不能和子矩形的边沿重合(即子矩形完全包裹子子矩形),在此条件下,找到 子矩形之和子子矩形之和 的最大值。

题解

显然我们可以预处理一个二维前缀和,这样就可以 O ( 1 ) O(1) O(1) 求出某个矩形的和。

子矩形我们可以 O ( n × m ) O(n\times m) O(n×m) 枚举,对于每个子矩形,我们只需要找到里面最小的子子矩形即可。由于子子矩形的大小固定,我们可以先横向跑一遍单调队列,再纵向跑一次单调队列,就可以求出每个子矩形里面的最小子子矩形了。

代码如下:

#include <cstdio>
#define maxn 1010

int n,m,a,b,c,d,sum[maxn][maxn];
int su[maxn][maxn],mi[maxn][maxn];
int getsum(int x,int y,int xx,int yy){return sum[xx][yy]-sum[xx][y-1]-sum[x-1][yy]+sum[x-1][y-1];}
struct node{int x,y;}q[maxn];
int st,ed,ans=0;
int max(int x,int y){return x>y?x:y;}

int main()
{
	scanf("%d %d %d %d %d %d",&n,&m,&a,&b,&c,&d);
	for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)scanf("%d",&sum[i][j]),sum[i][j]+=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];
	for(int i=c;i<=n;i++)for(int j=d;j<=m;j++)su[i][j]=getsum(i-c+1,j-d+1,i,j);
	for(int i=c+1;i<=n;i++)
	{
		st=1;ed=0;
		for(int j=1;j<=m;j++)
		{
			while(st<=ed&&q[st].y<=j-b+d)st++; while(st<=ed&&q[ed].x>su[i][j])ed--; q[++ed]=(node){su[i][j],j};
			if(j>=b-1)mi[i][j]=q[st].x;
		}
	}
	for(int j=b;j<=m;j++)
	{
		st=1;ed=0;
		for(int i=2;i<=n;i++)
		{
			while(st<=ed&&q[st].y<=i-a+c)st++; while(st<=ed&&q[ed].x>mi[i-1][j-1])ed--; q[++ed]=(node){mi[i-1][j-1],i-1};
			if(i>=a)ans=max(ans,getsum(i-a+1,j-b+1,i,j)-q[st].x);
		}
	}
	printf("%d",ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值