Usaco Training Section 5.3 Big Barn

在一个n*n的方格中找出最大的不包含障碍的正方形。(n<=1000,障碍数<=10000)

看起来好像有点难,不能随便枚举。但仔细一想,可以发现:最大的正方形中至少有一个的一边靠着障碍。于是我们只需枚举每个障碍上下左右最大的正方形是多少。

至于某一个方向最大的正方形怎么求,我们以右边的为例。我们先预处理每一列所有障碍的横坐标,排序。对于当前障碍在x行y列,j从y+1列向右推,每次用y列中最接近x的横坐标更新l,r,用l和r更新maxl和minr,正方形的边长为min(minr-maxl,j-b[i].y)。一个优化:当minr-maxl<j-b[i].y时可推出,因为再往右推的结果<=当前的minr-maxl。

注意:多余数组不要乱开,usaco training给的空间只有16MB

#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define inf 2147483647
#define mp make_pair
#define pii pair<int,int>
#define pb push_back
using namespace std;

inline int read(){
	int x=0;char c=getchar();
	while(c<'0'||c>'9') c=getchar();
	while(c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
	return x;
}

struct node{
	int x,y;
}b[10001];
int a[1001][1001];
vector<int> x[1001],y[1001];

inline bool cmp(node c,node d){
	return c.x<d.x;
}
inline bool cmp2(node c,node d){
	return c.y<d.y;
}

int main()
{
	freopen("bigbrn.in","r",stdin);
	freopen("bigbrn.out","w",stdout);
	int n=read(),m=read();
	for(int i=1;i<=m;++i) b[i].x=read(),b[i].y=read(),a[b[i].x][b[i].y]=1;
	sort(b+1,b+m+1,cmp);
	for(int i=1;i<=m;++i) x[b[i].y].pb(b[i].x);
	sort(b+1,b+m+1,cmp2);
	for(int i=1;i<=m;++i) y[b[i].x].pb(b[i].y);
	int ans=0;
	for(int i=1;i<=m;++i){
		int j=b[i].y+1,ml,mr;
		ml=0,mr=n+1;
		while(1){
			if(j>n) break;
			int pos,sz=x[j].size(),l,r;
			if(sz==0) l=1,r=n;
			else{
				pos=lower_bound(x[j].begin(),x[j].end(),b[i].x)-x[j].begin();
				if(pos==0) l=1,r=x[j][0]-1;
				else if(pos==sz) l=x[j][pos-1]+1,r=n;
				else l=x[j][pos-1]+1,r=x[j][pos]-1;
			}
			ml=max(ml,l);mr=min(mr,r);
			ans=max(ans,min(mr-ml+1,j-b[i].y));
			if(mr-ml+1<j-b[i].y) break;
			++j;
		}
		j=b[i].y-1;ml=0,mr=n+1;
		while(1){
			if(j<1) break;
			int pos,sz=x[j].size(),l,r;
			if(sz==0) l=1,r=n;
			else{
				pos=lower_bound(x[j].begin(),x[j].end(),b[i].x)-x[j].begin();
				if(pos==0) l=1,r=x[j][0]-1;
				else if(pos==sz) l=x[j][pos-1]+1,r=n;
				else l=x[j][pos-1]+1,r=x[j][pos]-1;
			}
			ml=max(ml,l);mr=min(mr,r);
			ans=max(ans,min(mr-ml+1,b[i].y-j));
			if(mr-ml+1<b[i].y-j) break;
			--j;
		}
		j=b[i].x+1;ml=0,mr=n+1;
		while(1){
			if(j>n) break;
			int pos,sz=y[j].size(),l,r;
			if(sz==0) l=1,r=n;
			else{
				pos=lower_bound(y[j].begin(),y[j].end(),b[i].y)-y[j].begin();
				if(pos==0) l=1,r=y[j][0]-1;
				else if(pos==sz) l=y[j][pos-1]+1,r=n;
				else l=y[j][pos-1]+1,r=y[j][pos]-1;
			}
			ml=max(ml,l);mr=min(mr,r);
			ans=max(ans,min(mr-ml+1,j-b[i].x));
			if(mr-ml+1<j-b[i].x) break;
			++j;
		}
		j=b[i].x-1;ml=0,mr=n+1;
		while(1){
			if(j<1) break;
			int pos,sz=y[j].size(),l,r;
			if(sz==0) l=1,r=n;
			else{
				pos=lower_bound(y[j].begin(),y[j].end(),b[i].y)-y[j].begin();
				if(pos==0) l=1,r=y[j][0]-1;
				else if(pos==sz) l=y[j][pos-1]+1,r=n;
				else l=y[j][pos-1]+1,r=y[j][pos]-1;
			}
			ml=max(ml,l);mr=min(mr,r);
			ans=max(ans,min(mr-ml+1,b[i].x-j));
			if(mr-ml+1<b[i].x-j) break;
			--j;
		}
	}
	printf("%d\n",ans);
	return 0; 
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值