把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

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
*/
/*
....#... 
#.....#. 
....#... 
.....@.. 
.#...... 
...#.... 
........ 
*/
posted @ 2020-11-25 22:24  Starlight_Glimmer  阅读(98)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end