【单调队列】[CQBZOJ2436]Zj 之 XX 洗浴
题目描述
众所周知,这个就不扩展了……
自从这次被抓以后,zj同学很不服气。回家后,zj 就开始了他的计划!(你不让我
洗浴,我就在家洗个痛快!)zj 打算在自家的院子里修一个洗浴池,当然他希望洗
浴池越大越好。但是院子里有 zj喜欢的一些植物,他不想毁掉任一颗植物,所以洗浴池不能将植物的位置占掉。zj的院子和洗浴池都是矩形的,浴池要完全处在院
子里,并且浴池的轮廓要与院子的轮廓平行或重合。每个植物可以看做一个点,浴
池不能覆盖任何一个植物点,但是植物点可以在浴池的边上,请问 zj 的浴池最大
能修多大.
输入
输入文件的第一行包含两个整数l和w,分别表示院子的长和宽。文件的第二行包
含一个整数n,表示植物的数量。以下n行每行包含两个整数x和y,表示一个植物
点的坐标。所有植物点都位于院子内,即:0 ≤ x ≤ l,0 ≤ y ≤ w。
输出
输出文件仅一行,包含一个整数s,表示浴池的最大面积。
样例输入
Copy (如果复制到控制台无换行,可以先粘贴到文本编辑器,再复制)
10 10
4
1 1
9 1
1 9
9 9
样例输出
80
提示
【数据范围】
0 ≤ n ≤ 5000,1 ≤ l ,w ≤ 30000
来源
HZOI
对每一行的点,找出它到该列上方最近的植物点的距离,可以看做矩形的最大宽度,然后向左右延伸,对每一行都求出一个最大的矩形。所有行的最大矩形即为答案。
总的时间复杂度为O(N^N).
本题点的坐标范围太大,需要先进行离散化。
要用单调队列或者堆栈进行优化。
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
#define MAXN 5000
bool mat[MAXN+10][MAXN+10];
void Read(int &x){
char c;
while(c=getchar(),c!=EOF)
if(c>='0'&&c<='9'){
x=c-'0';
while(c=getchar(),c>='0'&&c<='9')
x=x*10+c-'0';
ungetc(c,stdin);
return;
}
}
int l,w,n,ans,x[MAXN+10],cntx,cnty,y[MAXN+10],h[MAXN+10],s[MAXN+10],front,rear;
pair<int,int>a[MAXN+10],q[MAXN+10];
void init(){
Read(l),Read(w),Read(n);
int i;
for(i=1;i<=n;i++){
Read(a[i].first),Read(a[i].second);
x[i]=a[i].first;
y[i]=a[i].second;
}
x[n+1]=l;
y[n+1]=w;
sort(x+1,x+n+2);
cntx=unique(x,x+n+2)-x-1;
sort(y+1,y+n+2);
cnty=unique(y,y+n+2)-y-1;
for(i=1;i<=n;i++)
mat[lower_bound(x+1,x+cntx+1,a[i].first)-x][lower_bound(y+1,y+cnty+1,a[i].second)-y]=1;
}
void solve(){
int i,j;
for(i=1;i<=cntx;i++){
front=1,rear=0;
q[0]=make_pair(0,0);
for(j=1;j<=cnty;j++){
h[j]+=x[i]-x[i-1];
while(front<=rear&&q[rear].first>=h[j])
rear--;
s[j]=h[j]*(y[j]-q[rear].second);
q[++rear]=make_pair(h[j],y[j]);
}
front=1,rear=0;
q[0]=make_pair(0,w);
for(j=cnty-1;j;j--){
while(front<=rear&&q[rear].first>h[j])
rear--;
s[j]+=h[j]*(q[rear].second-y[j]);
q[++rear]=make_pair(h[j],y[j]);
ans=max(ans,s[j]);
if(mat[i][j])
h[j]=0;
}
}
for(j=1;j<=cnty;j++)
ans=max(ans,l*(y[j]-y[j-1]));
}
int main()
{
init();
solve();
printf("%d",ans);
}