●CodeForces 480E Parking Lot

题链:

http://codeforces.com/problemset/problem/480/E
题解:

单调队列,逆向思维
(在线的话应该是分治做,但是好麻烦。。)
离线操作,逆向考虑,
最后的状态可以用O(N*M)的dp得出最大正方形边长。
然后反向一个一个的把障碍变回非障碍,显然答案不会变小。
维护好up[i][j],down[i][j],分别表示从(i,j)位置向上向下有多长的连续非障碍。
不难发现,如果有更大的答案的话,那么必然包含当前改变的位置的那一行的某些格子。
所以确定了在这一行上寻找是否有更大的答案,
然后就用单调队列维护来求出当前行上的最大正方形即可。


代码:

#include<bits/stdc++.h>
#define MAXN 2005
using namespace std;
struct CMD{int x,y,ans;}C[MAXN];
bool graph[MAXN][MAXN];
int up[MAXN][MAXN],down[MAXN][MAXN];
int N,M,K,ANS;
void update(int j){
	for(int i=1;i<=N;i++)
		up[i][j]=graph[i][j]?up[i-1][j]+1:0;
	for(int i=N;i>=1;i--)
		down[i][j]=graph[i][j]?down[i+1][j]+1:0;
}
void prework(){
	static int dp[MAXN][MAXN];
	for(int i=1;i<=N;i++)
		for(int j=1;j<=M;j++){
			if(graph[i][j]==0) dp[i][j]=0;
			else dp[i][j]=min(dp[i-1][j-1],min(dp[i-1][j],dp[i][j-1]))+1;
			ANS=max(ANS,dp[i][j]);
		}
	for(int j=1;j<=M;j++) update(j);
}
int main(){
	char ch;
	scanf("%d%d%d",&N,&M,&K);
	for(int i=1;i<=N;i++)
		for(int j=1;j<=M;j++)
			scanf(" %c",&ch),graph[i][j]=ch=='.'?1:0;
	for(int i=1;i<=K;i++)
		scanf("%d%d",&C[i].x,&C[i].y),graph[C[i].x][C[i].y]=0;
	prework(); C[K].ans=ANS;
	static int ql[MAXN],qr[MAXN],qll,qlr,qrl,qrr;
	for(int i=K,ret,x,l,r,h;i>=1;i--){
		ret=0; x=C[i].x;
		graph[C[i].x][C[i].y]=1;
		update(C[i].y);
		qll=qrl=l=1; qlr=qrr=r=0;
		while(l<=M){
			h=-233;
			if(l<=r){
				h=up[x][ql[qll]]+down[x][qr[qrl]]-1;
				ret=max(ret,min(h,r-l+1));
			}
			if(r<M&&(h==-233||h>=r-l+1)){
				r++;
				while(qll<=qlr&&up[x][ql[qlr]]>=up[x][r]) qlr--; ql[++qlr]=r;
				while(qrl<=qrr&&down[x][qr[qrr]]>=down[x][r]) qrr--;qr[++qrr]=r;
			}
			else{
				l++;
				while(qll<=qlr&&ql[qll]<l) qll++;
				while(qrl<=qrr&&qr[qrl]<l) qrl++;
			}
		}
		ANS=max(ret,ANS);
		C[i-1].ans=ANS;
	}
	for(int i=1;i<=K;i++) printf("%d
",C[i].ans);	
	return 0;
}

  

原文地址:https://www.cnblogs.com/zj75211/p/8541900.html