Parking Lot (CodeForces - 480E) (单调队列/线段树)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/qq_37555704/article/details/102648416

文章目录

题目

CF
题目大意:
n m n∗m 的矩阵,有一些点不能选。 k k 次操作,每次都让一个点变成不可选,每次都问当前可选的最大正方形。
n , m , k 2000 n,m,k\le2000

思路

首先,倒着来变为让一个点从不可选变为可选显然好做一些
在这里插入图片描述
首先倒着做答案肯定是单调不递减的,然后我们假设此次更改的点坐标是 ( x i , y i ) (x_i,y_i) ,如果答案更新的话,新的答案构成的矩形肯定会包含 ( x i , y i ) (x_i,y_i) 这个点我们假设当前答案是 a n s ans 于是我们要尝试 a n s + 1 ans+1 是否可以(因为似乎一步确定 a n s ans 有些困难),我们不妨把每个点向上最高和向下最深能到达的坐标记录为 h i g h [ i ] [ j ] high[i][j] l o w [ i ] [ j ] low[i][j] (代码里好像写反了。。。),于是相当于查询包含这个点的宽度为 a n s + 1 ans+1 的区间最值查询,如果我们采用线段树,那么时间复杂度为 O ( n 2 l o g n ) O(n^2logn) ,可过
但是这也是单调队列经典应用,时间复杂度 O ( n 2 ) O(n^2)

代码

//#pragma GCC optimize(2)
#include<set>
#include<map>
#include<stack>
#include<cmath>
#include<queue>
#include<cstdio>
#include<vector>
#include<climits>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define LL long long
using namespace std;
int read(){
	bool f=0;int x=0;char c=getchar();
	while(c<'0'||'9'<c){if(c=='-')f=1;c=getchar();}
	while('0'<=c&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
	return !f?x:-x;
}
#define lch (i<<1)
#define rch (i<<1|1)
#define MAXN 2000
#define INF 0x3f3f3f3f
char S[MAXN+5][MAXN+5];
int m,high[MAXN+5][MAXN+5],low[MAXN+5][MAXN+5];
int Q[MAXN+5],X[MAXN+5],Y[MAXN+5],Ans[MAXN+5],a[MAXN+5];
bool check(int row,int len){
	int head=1,tail=0;
	for(int j=1;j<=m;j++){
		while(head<=tail&&high[row][Q[tail]]>=high[row][j]) tail--;
		Q[++tail]=j;
		while(Q[head]+len<=j) head++;
		a[j]=high[row][Q[head]];
	}
	head=1,tail=0;
	for(int j=1;j<=m;j++){
		while(head<=tail&&low[row][Q[tail]]>=low[row][j]) tail--;
		Q[++tail]=j;
		while(Q[head]+len<=j) head++;
		a[j]+=low[row][Q[head]]-1;
	}
	for(int j=len;j<=m;j++)
		if(a[j]>=len) return 1;
	return 0;
}
int f[MAXN+5][MAXN+5];
int main(){
	int n=read();m=read();int q=read();
	for(int i=1;i<=n;i++)
		scanf("%s",S[i]+1);
	for(int i=1;i<=q;i++){
		X[i]=read(),Y[i]=read();
		S[X[i]][Y[i]]='X';
	}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			if(S[i][j]=='X') high[i][j]=0;
			else high[i][j]=high[i-1][j]+1;
	for(int i=n;i>=1;i--)
		for(int j=1;j<=m;j++)
			if(S[i][j]=='X') low[i][j]=0;
			else low[i][j]=low[i+1][j]+1;
	int 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],min(f[i][j-1],f[i-1][j-1]))+1;
			ans=max(ans,f[i][j]);
		}
	for(int t=q;t>=1;t--){
		Ans[t]=ans;
		S[X[t]][Y[t]]='.';
		for(int i=1;i<=n;i++)
			if(S[i][Y[t]]=='X') high[i][Y[t]]=0;
			else high[i][Y[t]]=high[i-1][Y[t]]+1;
		for(int i=n;i>=1;i--)//
			if(S[i][Y[t]]=='X') low[i][Y[t]]=0;
			else low[i][Y[t]]=low[i+1][Y[t]]+1;
		while(check(X[t],ans+1)) ans++;
	}
	for(int i=1;i<=q;i++)
		printf("%d\n",Ans[i]);
	return 0;
}

思考

以后不要求强制在线一定要考虑离线,对于单调队列解决的问题一定要熟悉

猜你喜欢

转载自blog.csdn.net/qq_37555704/article/details/102648416