传送门
题解:
为什么我会在某人的数据结构讲义里面看到这道题。。。
首先我们可以DP求出最大子正方形,设 f [ i ] [ j ] f[i][j] f[i][j]表示以 i , j i,j i,j为右下角的最大正方形边长,则对于合法格子的转移就是 f [ i ] [ j ] = min { f [ i ] [ j − 1 ] , f [ i − 1 ] [ j ] , f [ i − 1 ] [ j − 1 ] } + 1 f[i][j]=\min\{f[i][j-1],f[i-1][j],f[i-1][j-1]\}+1 f[i][j]=min{f[i][j−1],f[i−1][j],f[i−1][j−1]}+1
但是这个DP似乎并不资瓷修改。
将询问离线,考虑新加一个合法点,如果它能够更新答案,新的最大子正方形必然包含这个点。
我们处理每个点向上遇到的一个障碍的位置,每个点向下遇到的第一个障碍的位置。
然后我们处理出被修改的点所在的这一行的每个位置向上向下遇到的第一个障碍的位置,并求出以修改点为中心的两个东西的前后缀最大最小值。
然后暴力枚举右端点check一下答案能否增加就行了。
好写常数小,一发AC,CF rk2
代码:
#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const
using std::cerr;
using std::cout;
cs int N=2e3+7;
int n,m,k,Ans;
int g[N][N],f[N][N];
int u[N][N],d[N][N];
int x[N],y[N];
int gu[N],gd[N];
int ans[N];
signed main(){
#ifdef zxyoi
freopen("parking.in","r",stdin);
#endif
scanf("%d%d%d",&n,&m,&k);
for(int re i=1;i<=n;++i){
getchar();
for(int re j=1;j<=m;++j)
g[i][j]=(getchar()=='X');
}
for(int re i=1;i<=k;++i){
scanf("%d%d",x+i,y+i);
g[x[i]][y[i]]=1;
}
for(int re i=0;i<=n+1;++i)
for(int re j=0;j<=m+1;++j)
u[i][j]=d[i][j]=i;
for(int re i=1;i<=n;++i)
for(int re j=1;j<=m;++j)
if(!g[i][j])u[i][j]=u[i-1][j];
for(int re i=n;i;--i)
for(int re j=1;j<=m;++j)
if(!g[i][j])d[i][j]=d[i+1][j];
for(int re i=1;i<=n;++i)
for(int re j=1;j<=m;++j)
if(!g[i][j]){
f[i][j]=std::min(f[i-1][j-1],std::min(f[i-1][j],f[i][j-1]))+1;
Ans=std::max(Ans,f[i][j]);
}
for(int re t=k;t;--t){
int x0=x[t],y0=y[t];
g[x0][y0]=0,ans[t]=Ans;
for(int re i=x0;i<=n;++i)
if(!g[i][y0])u[i][y0]=u[i-1][y0];
else break;
for(int re i=x0;i;--i)
if(!g[i][y0])d[i][y0]=d[i+1][y0];
else break;
gu[y0]=u[x0][y0];gd[y0]=d[x0][y0];
for(int re i=y0-1;i;--i){
gu[i]=std::max(gu[i+1],u[x0][i]);
gd[i]=std::min(gd[i+1],d[x0][i]);
}
for(int re i=y0+1;i<=m;++i){
gu[i]=std::max(gu[i-1],u[x0][i]);
gd[i]=std::min(gd[i-1],d[x0][i]);
}
for(int re i=y0;i<=m;++i)
while(i-Ans>0&&std::min(gd[i],gd[i-Ans])-std::max(gu[i],gu[i-Ans])-1>Ans)++Ans;
}
for(int re i=1;i<=k;++i)cout<<ans[i]<<"\n";
return 0;
}