• 并不对劲的CF480E:Parking Lot


    题目大意

    有一个(n imes m)的网格,每个位置是黑色或者白色。(k)个操作,每个操作是将一个白格子染黑,操作后输出当前最大的白色正方形的边长。(n,m,kleq 2 imes 10^3)

    题解

    发现在每次操作是把白格子变黑,会使答案变小。维护“变小的最大值”听上去不太舒服。考虑把操作全都反过来,变成把黑格子染白。
    这样每次操作之后,如果答案变大了,那么新的答案正方形一定包含在被操作的格子。
    考虑对每个点记它左边最左的白格子和右边最右的白格子,操作时暴力更新与被操作点同行的点。
    答案就是想找连续的一段与被操作的点在同一列,“段的长度”与“最左的右边界-最右的左边界”的最小值尽可能大。
    发现可以判断答案是否大于一个数(x):当这一列上存在一个点,满足该点到从该点往上数第(x)个点满足“最左的右边界-最右的左边界”不少于(x)(x)就可以;反之就不可以。
    可以用线段树或单调队列维护区间最左右边界和最右左边界。
    这题知道判断解是否合法的方法后也不用二分,因为在处理过后答案就是不降的,而且不会超过(min(n,m)),而判断能否使答案增加1需要(Theta(n))(Theta(nspace logspace n))的时间复杂度,所以可以每次暴力判断能否使答案增加。
    总时间复杂度(Theta(n imes m+k imes m+k imes n))

    代码

    #include<algorithm>
    #include<cmath>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<ctime>
    #include<iomanip>
    #include<iostream>
    #include<map>
    #include<queue>
    #include<stack>
    #include<vector>
    #define LL long long
    #define rep(i,x,y) for(int i=(x);i<=(y);++i)
    #define dwn(i,x,y) for(int i=(x);i>=(y);--i)
    #define view(u,k) for(int k=fir[u];~k;k=nxt[k])
    #define maxn 2007
    using namespace std;
    int read()
    {
    	int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)&&ch!='-')ch=getchar();
    	if(ch=='-')f=-1,ch=getchar();
    	while(isdigit(ch))x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
    	return x*f;
    }
    void write(int x)
    {
    	char ch[20];int f=0;
    	if(!x){putchar('0'),putchar('
    ');return;}
    	if(x<0)putchar('-'),x=-x;
    	while(x)ch[++f]=x%10+'0',x/=10;
    	while(f)putchar(ch[f--]);
    	putchar('
    ');
    }
    int qx[maxn],qy[maxn],q[maxn],hd,tl; 
    int col[maxn][maxn],ans,lmx[maxn][maxn],rmx[maxn][maxn],n,m,k,res[maxn],tmp[maxn],dp[maxn][maxn];
    char s[maxn];
    int jud(int yy)
    {
    	hd=1,tl=0;
    	rep(i,1,n)
    	{
    		while(hd<=tl&&q[hd]<i-(ans+1)+1)hd++;
    		while(hd<=tl&&lmx[q[tl]][yy]<=lmx[i][yy])tl--;
    		q[++tl]=i;
    		if(i<ans+1)continue;
    		tmp[i]=yy-lmx[q[hd]][yy]+1;
    	}
    	hd=1,tl=0;
    	rep(i,1,n)
    	{
    		while(hd<=tl&&q[hd]<i-(ans+1)+1)hd++;
    		while(hd<=tl&&rmx[q[tl]][yy]>=rmx[i][yy])tl--;
    		q[++tl]=i;
    		if(i<ans+1)continue;
    		tmp[i]+=rmx[q[hd]][yy]-yy;
    	}
    	rep(i,ans+1,n)if(tmp[i]>=ans+1)return 1;
    	return 0; 
    }
    int main()
    {
    	n=read(),m=read(),k=read();
    	rep(i,1,n)
    	{
    		scanf("%s",s+1);
    		rep(j,1,m)if(s[j]!='.')col[i][j]=1; 
    	}
    	rep(i,1,k)qx[i]=read(),qy[i]=read(),col[qx[i]][qy[i]]=1;
    	rep(i,1,n)
    	{
    		rep(j,1,m)
    		{
    			if(col[i][j]){lmx[i][j]=j+1;continue;}
    			dp[i][j]=min(min(dp[i-1][j],dp[i][j-1]),dp[i-1][j-1])+1;
    			ans=max(dp[i][j],ans);
    			if(j==1||col[i][j-1])lmx[i][j]=j;
    			else lmx[i][j]=lmx[i][j-1];
    		}
    		dwn(j,m,1)
    		{
    			if(col[i][j]){rmx[i][j]=j-1;continue;}
    			if(j==m||col[i][j+1])rmx[i][j]=j;
    			else rmx[i][j]=rmx[i][j+1];
    		}
    	}
    	dwn(i,k,1)
    	{
    		res[i]=ans;
    		col[qx[i]][qy[i]]=0;
    		int nl=qy[i],nr=qy[i];
    		while(nl-1>=1&&!col[qx[i]][nl-1])nl--;
    		while(nr+1<=m&&!col[qx[i]][nr+1])nr++;
    		rep(j,nl,nr)lmx[qx[i]][j]=nl,rmx[qx[i]][j]=nr;
    		
    		while(jud(qy[i]))ans++;
    	}
    	rep(i,1,k)write(res[i]);
    	return (0-0);
    }
    
  • 相关阅读:
    洛谷月赛 Hello World(升级版)
    codevs1001 舒适的路线
    vijos & codevs 能量项链
    vijos 运输计划
    noip2016普及组题解和心得
    UVa 10891 Game of Sum
    UVa 10635 Prince and Princess
    某模拟题题解 2016.11.17
    贪心入门题
    某模拟题题解 2016.11.16
  • 原文地址:https://www.cnblogs.com/xzyf/p/11631689.html
Copyright © 2020-2023  润新知