题链:
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; }