CF480E Parking Lot【最大子正方形-带修改】
(博客园第\(100\)篇博客合影~
题目解析
被拿来作为考试题,我以为我会做来着,然而并不会(怎么好多人都做过这道题,果然是我太菜了嘤嘤嘤
(三种做法的代码都放在了最后面
法一
如果你什么都不会,就像我一样,那么可以先敲出一个大暴力出来。
\(a[i][j]\)表示点\((i,j)\)前面一列最大的连续空地长度,然后\(n^2\)枚举每个点作为正方形左上角,再枚举一个变量表示往右边延伸多少,一边枚举一边判断。
(其实是因为想起了 这道题
容易发现,这种方法求答案是\(n^3\)的(虽然普通情况下不会跑满),加上询问总时间复杂度\(n^4\)
然后你就得到了\(20pts\)的好成绩(要知道全场只有我一个人得到了这个奇怪的分数
考场上尝试优化,因为每次修改一个点之后,只有这个点周围的答案会改变,因为这个点可能把原来的答案正方形劈成很多半。但是发现有可能会存在很多个答案正方形,修改之后你无法判断这一坨会不会影响答案。
法二
怎么大家都会这种\(dp\)的方法啊
定义\(f[i][j]\)表示以点\((i,j)\)为右下角的最大正方形的边长。
如果\((i,j)\)是障碍点,那么\(f[i][j]=0\),否则有\(f[i][j]=min(f[i-1][j-1],min(f[i-1][j],f[i][j-1]))+1\)
这个也比较好理解,考虑\((i-1,j-1)\)往右下移一格,而\((i-1,j),(i,j-1)\)的答案也会造成限制,所以取最小值,再加上拓展出来的\(1\)
这个做法修改\(O(1)\),每次查询\(O(n^2)\),总时间复杂度\(O(n^3)\)
然后你就得到了\(50pts\)的好成绩(大众分
法三
我们想起了法一那个中道崩殂的优化,那个优化不可行,主要是因为修改之后最大值会变小,而我们不知道别的地方有没有其它最大值。
但如果把询问离线下来,把加障碍变成减障碍,那么最大值就只会变大,而这个变大的最大值只能来源于修改的那一部分(冤有头债有主),所以就可以只处理修改的那一部分了。
具体怎么求答案呢?
如果有更大的答案的话,那么一定是包含这个障碍点的,也就是跨过这个障碍点所在行、所在列的。我们不妨从列的角度来考虑,先类似于悬线法那样预处理出每个点最多能够向左右延伸多远,这个在修改时可以\(O(n)\)更新。查询时,由于修改点那一列一定在答案正方形内部,我们可以用单调队列来维护这一列向左向右延伸的距离,如果队首限制了正方形的发展,就弹出队首。由于需要一个具体的限制,所以可以用\(check\)的方式,去\(check\)更大的答案是否可行。平均下来查询大概是\(O(n)\)的吧,可能常数会有点大,总复杂度\(O(n^2)\)。
而如果真用悬线法的话,更新参数只需要更新去掉障碍点那一行,但是算答案还是要全部遍历一遍,因为悬线法是算的每个点对应的悬线对应的最大正方形,而更改了这个障碍点可能会影响到它下半部分任何一个点(我刚开始还以为只会影响这一列来着)。
然后你就得到了\(100pts\)的真·好成绩
►Code View Ver.1
#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
#include<cstring>
using namespace std;
#define N 2005
#define INF 0x3f3f3f3f
#define LL long long
int rd()
{
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
return f*x;
}
int n,m,T;
char s[N][N];
int a[N][N],ans;
int calc()
{
int res=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
int p=j,len=a[i][j];
while(p-j+1<=len)
{
len=min(len,a[i][++p]),res=max(res,min(p-j+1,len));
if(res==ans) return ans;
}
}
return ans=res;
}
int main()
{
freopen("parking.in","r",stdin);
freopen("parking.out","w",stdout);
n=rd(),m=rd(),T=rd();
for(int i=1;i<=n;i++)
scanf("%s",s[i]+1);
for(int i=1;i<=m;i++)
if(s[1][i]=='.') a[1][i]=1;
for(int i=2;i<=n;i++)
for(int j=1;j<=m;j++)
if(s[i][j]=='.')
a[i][j]=a[i-1][j]+1;
while(T--)
{
int x=rd(),y=rd();
for(int i=x+1;i<=n;i++)
{
if(!a[i][y]) break;
a[i][y]-=a[x][y];
}
a[x][y]=0;
calc();
printf("%d\n",ans);
}
return 0;
}
►Code View Ver.2
#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
#include<cstring>
using namespace std;
#define N 2005
#define INF 0x3f3f3f3f
#define LL long long
int rd()
{
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
return f*x;
}
int n,m,T;
char s[N][N];
int f[N][N],ans;
void dp()
{
ans=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
if(s[i][j]=='X') f[i][j]=0;
else f[i][j]=min(f[i-1][j-1],min(f[i-1][j],f[i][j-1]))+1;
ans=max(ans,f[i][j]);
}
}
int main()
{
freopen("parking.in","r",stdin);
freopen("parking.out","w",stdout);
n=rd(),m=rd(),T=rd();
for(int i=1;i<=n;i++)
scanf("%s",s[i]+1);
while(T--)
{
int x=rd(),y=rd();
s[x][y]='X';
dp();
printf("%d\n",ans);
}
return 0;
}
►Code View Ver.3
#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
#include<cstring>
using namespace std;
#define N 2005
#define INF 0x3f3f3f3f
#define LL long long
int rd()
{
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
return f*x;
}
int n,m,T;
char s[N][N];
int f[N][N],ans,res[N];
int l[N][N],r[N][N];
pair<int,int> q[N];
int xx,yy;
int Q[N],hd,tl,tmp[N];
void dp()
{
ans=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
if(s[i][j]=='X') f[i][j]=0;
else f[i][j]=min(f[i-1][j-1],min(f[i-1][j],f[i][j-1]))+1;
ans=max(ans,f[i][j]);
}
}
void Init()
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(s[i][j]=='X') l[i][j]=0;
else l[i][j]=l[i][j-1]+1;
}
for(int j=m;j>=1;j--)
{
if(s[i][j]=='X') r[i][j]=0;
else r[i][j]=r[i][j+1]+1;
}
}
}
bool check(int x)
{
hd=1,tl=0;
for(int i=1;i<=n;i++)
{//维护左边
while(hd<=tl&&l[Q[tl]][yy]>=l[i][yy]) tl--;
//维护单调增队列 如果当前可到位置比队列里的大 那么队列里的元素就不会成为瓶颈
Q[++tl]=i;
while(hd<=tl&&Q[hd]<=i-x) hd++;//队首成为了限制
tmp[i]=l[Q[hd]][yy];
}
hd=1,tl=0;
for(int i=1;i<=n;i++)
{//维护右边
while(hd<=tl&&r[Q[tl]][yy]>=r[i][yy]) tl--;
Q[++tl]=i;
while(hd<=tl&&Q[hd]<=i-x) hd++;
tmp[i]+=r[Q[hd]][yy]-1;
}
for(int i=x;i<=n;i++)
if(tmp[i]>=x) return 1;
return 0;
}
int main()
{
freopen("parking.in","r",stdin);
freopen("parking.out","w",stdout);
n=rd(),m=rd(),T=rd();
for(int i=1;i<=n;i++)
scanf("%s",s[i]+1);
for(int i=1;i<=T;i++)
{
q[i].first=rd(),q[i].second=rd();
s[q[i].first][q[i].second]='X';
}
dp();
res[T]=ans;
Init();
for(int i=T-1;i>=1;i--)
{
xx=q[i+1].first,yy=q[i+1].second;
s[xx][yy]='.';
for(int j=1;j<=m;j++)
{
if(s[xx][j]=='X') l[xx][j]=0;
else l[xx][j]=l[xx][j-1]+1;
}
for(int j=m;j>=1;j--)
{
if(s[xx][j]=='X') r[xx][j]=0;
else r[xx][j]=r[xx][j+1]+1;
}
while(check(ans+1)) ans++;
res[i]=ans;
}
for(int i=1;i<=T;i++)
printf("%d\n",res[i]);
return 0;
}
►Code View Ver.4 悬线法 TLE
#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
#include<cstring>
using namespace std;
#define N 2005
#define INF 0x3f3f3f3f
#define LL long long
int rd()
{
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
return f*x;
}
int n,m,T;
char s[N][N];
int ans,res[N];
int l[N][N],r[N][N],h[N][N],L[N][N],R[N][N];
pair<int,int> q[N];
void Init(int x)
{
int t=1;
for(int j=1;j<=m;j++)
{
if(s[x][j]=='.') l[x][j]=t;
else L[x][j]=0,t=j+1;
}
t=m;
for(int j=m;j>=1;j--)
{
if(s[x][j]=='.') r[x][j]=t;
else R[x][j]=m+1,t=j-1;
}
}
void work(int y)
{
for(int i=1;i<=n;i++)
if(s[i][y]=='.')
{
h[i][y]=h[i-1][y]+1;
L[i][y]=max(L[i-1][y],l[i][y]);
R[i][y]=min(R[i-1][y],r[i][y]);
int p=h[i][y],q=R[i][y]-L[i][y]+1;
ans=max(ans,min(p,q));
}
}
int main()
{
freopen("parking.in","r",stdin);
freopen("parking.out","w",stdout);
n=rd(),m=rd(),T=rd();
for(int i=1;i<=n;i++)
scanf("%s",s[i]+1);
for(int i=1;i<=T;i++)
{
q[i].first=rd(),q[i].second=rd();
s[q[i].first][q[i].second]='#';
}
for(int i=1;i<=n;i++)
Init(i);
for(int i=1;i<=m;i++)
R[0][i]=m+1;
for(int i=1;i<=m;i++)
work(i);
res[T]=ans;
for(int i=T-1;i>=1;i--)
{
s[q[i+1].first][q[i+1].second]='.';
Init(q[i+1].first);
for(int j=1;j<=m;j++)
work(j);
//work(q[i+1].second);
res[i]=ans;
}
for(int i=1;i<=T;i++)
printf("%d\n",res[i]);
return 0;
}
/*
7 8 4
........
#.....#.
........
........
.#......
........
........
1 5
6 4
3 5
4 6
*/
/*
....#...
#.....#.
....#...
.....@..
.#......
...#....
........
*/