【JZOJ4817】square【二维rmq】【二分】

本文介绍了一种高效的算法,用于在一个由0和1组成的矩阵中找到最大的全由1构成的正方形,并能快速响应子矩阵内的查询。通过二分查找和二维RMQ(Range Maximum Query)技术,实现了O(nmlognlogm+qlogn)的时间复杂度。
摘要由CSDN通过智能技术生成

题目

题目链接:https://jzoj.net/senior/#main/show/4817
给出一个 01 01 01矩阵,每次询问子矩阵中的最大 1 1 1正方形。


思路:

二分最大正方形的边长 m i d mid mid,那么对于询问 ( x , y ) ( x x , y y ) (x,y)(xx,yy) (x,y)(xx,yy)的子矩阵,我们其实就是要判断子矩阵 ( x + m i d − 1 , y + m i d − 1 ) ( x x , y y ) (x+mid-1,y+mid-1)(xx,yy) (x+mid1,y+mid1)(xx,yy)的最大值是否不小于 m i d mid mid
二维 r m q rmq rmq即可。
时间复杂度 O ( n m log ⁡ n log ⁡ m + q log ⁡ n ) O(nm\log n\log m+q\log n) O(nmlognlogm+qlogn)
注意 l o g ( x ) log(x) log(x)需要预处理而不是在二分中求,否则时间复杂度就变成了 O ( q log ⁡ 2 n ) O(q\log^2n) O(qlog2n)


代码:

#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
using namespace std;

const int N=1010,LG=10;
int n,m,Q,f[N][N],rmq[LG+1][LG+1][N][N],Log[N],power[LG+1];

int read()
{
	int d=0; char ch=getchar();
	while (!isdigit(ch)) ch=getchar();
	while (isdigit(ch)) d=(d<<3)+(d<<1)+ch-48,ch=getchar();
	return d;
}

inline int maxx(int wyc,int ak,int ioi,int orz)
{
	return max(max(wyc,ak),max(ioi,orz));
}

inline bool check(int x,int y,int xx,int yy,int k)
{
	if (x>xx || y>yy) return 0;
	int logx=Log[xx-x+1],logy=Log[yy-y+1];
	if (rmq[logx][logy][x][y]>=k || rmq[logx][logy][xx-power[logx]+1][yy-power[logy]+1]>=k) return 1;
	if (rmq[logx][logy][xx-power[logx]+1][y]>=k || rmq[logx][logy][x][yy-power[logy]+1]>=k) return 1;
	return 0;
}

int main()
{
	freopen("square.in","r",stdin);
	freopen("square.out","w",stdout);
	for (int i=2;i<=1000;i++)
		Log[i]=Log[i/2]+1;
	power[0]=1;
	for (int i=1;i<=10;i++)
		power[i]=power[i-1]*2;
	n=read(); m=read();
	for (int i=1,x;i<=n;i++)
		for (int j=1;j<=m;j++)
		{
			if (read()) f[i][j]=min(f[i][j-1],min(f[i-1][j],f[i-1][j-1]))+1;
			rmq[0][0][i][j]=f[i][j];
		}
	for (int k=0;k<=LG;k++)
		for (register int l=k?0:1;l<=LG;l++)
			for (register int i=1;(!k && i<=n) || (k && i+power[k-1]<=n);i++)
				for (register int j=1;(!l && j<=m) || (l && j+power[l-1]<=m);j++)
					if (!k)
						rmq[k][l][i][j]=max(rmq[k][l-1][i][j],rmq[k][l-1][i][j+power[l-1]]);
					else if (!l)
						rmq[k][l][i][j]=max(rmq[k-1][l][i][j],rmq[k-1][l][i+power[k-1]][j]);
					else
						rmq[k][l][i][j]=maxx(rmq[k-1][l-1][i][j],rmq[k-1][l-1][i+power[k-1]][j],rmq[k-1][l-1][i][j+power[l-1]],rmq[k-1][l-1][i+power[k-1]][j+power[l-1]]);
	Q=read();
	while (Q--)
	{
		int x,xx,y,yy,l=1,r=n,mid;
		x=read(); y=read(); xx=read(); yy=read();
		while (l<=r)
		{
			mid=(l+r)>>1;
			if (check(x+mid-1,y+mid-1,xx,yy,mid)) l=mid+1;
				else r=mid-1;
		}
		printf("%d\n",l-1);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值