题目大意
在N*M的网格中,有P个矩形建筑,求一个最大边长的正方形,使得网格中能找到一个放置正方形的地方,不会与建筑重合。
N,M<=10^6,P<=40000。
扫描线
如果有两条在x轴上的扫描线l与r,表示l~r-1之间可以放边长为r-l的正方形。
期望在l与r间放一个边长为r-l+1的矩形。
那么假如我们能在两条扫描线间找到最大空隙ms。
如果r-l+1<=ms,那么可以放,接下来r+1。
如果r-l+1>ms,便不能放,因此l+1。由于l~r-1之间可以放边长为r-l的正方形,l+1~r-1之间便可以放边长为r-l-1的正方形。所以r不用变。
错误的线段树
现在问题就是如何求空隙。
显然可以使用线段树,维护最大连续0。每个位置的数代表被几个障碍包含。
当r+1时,加入左边界在x=r上的矩形。
当l+1时,删除右边界在x=l-1上的矩形。
如何维护最大连续0?
我们可以维护num,lnum,rnum分别表示最大连续0,左起最大连续0,右起最大连续0。
显然
tree[p].num=max(tree[p*2].rnum+tree[p*2+1].lnum,max(tree[p*2].num,tree[p*2+1].num));
tree[p].lnum=tree[p*2].lnum;
if (tree[p*2].lnum==mid-l+1) tree[p].lnum+=tree[p*2+1].lnum;
tree[p].rnum=tree[p*2+1].rnum;
if (tree[p*2+1].rnum==r-mid) tree[p].rnum+=tree[p*2].rnum;
注意到这道题要支持区间添加与区间删减,且删减区间与添加区间一一对应,再加上一个区间只要有add标记那么最大连续0一定为0,所以我们可以不下传标记,打一个“错误”的线段树。
#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=1000000+10,maxp=400000+10;
struct dong{
int x1,x2,y1,y2;
};
struct suan{
int num,lnum,rnum,add;
};
dong a[maxp];
suan tree[maxn*10];
int h[maxn],next[maxn*2],go[maxn*2];
int i,j,k,l,r,t,n,m,p,tot,ans,ms;
void inse(int x,int y){
go[++tot]=y;
next[tot]=h[x];
h[x]=tot;
}
void build(int p,int l,int r){
if (l==r){
tree[p].num=tree[p].lnum=tree[p].rnum=1;
return;
}
int mid=(l+r)/2;
build(p*2,l,mid);
build(p*2+1,mid+1,r);
tree[p].num=tree[p].lnum=tree[p].rnum=tree[p*2].num+tree[p*2+1].num;
}
void add(int p,int l,int r,int a,int b){
if (l==a&&r==b){
tree[p].add++;
tree[p].num=tree[p].lnum=tree[p].rnum=0;
return;
}
int mid=(l+r)/2;
if (b<=mid) add(p*2,l,mid,a,b);
else if (a>mid) add(p*2+1,mid+1,r,a,b);
else{
add(p*2,l,mid,a,mid);
add(p*2+1,mid+1,r,mid+1,b);
}
if (!tree[p].add){
tree[p].num=max(tree[p*2].rnum+tree[p*2+1].lnum,max(tree[p*2].num,tree[p*2+1].num));
tree[p].lnum=tree[p*2].lnum;
if (tree[p*2].lnum==mid-l+1) tree[p].lnum+=tree[p*2+1].lnum;
tree[p].rnum=tree[p*2+1].rnum;
if (tree[p*2+1].rnum==r-mid) tree[p].rnum+=tree[p*2].rnum;
}
}
void sub(int p,int l,int r,int a,int b){
int mid=(l+r)/2;
if (l==a&&r==b){
tree[p].add--;
if (!tree[p].add){
if (l!=r){
tree[p].num=max(tree[p*2].rnum+tree[p*2+1].lnum,max(tree[p*2].num,tree[p*2+1].num));
tree[p].lnum=tree[p*2].lnum;
if (tree[p*2].lnum==mid-l+1) tree[p].lnum+=tree[p*2+1].lnum;
tree[p].rnum=tree[p*2+1].rnum;
if (tree[p*2+1].rnum==r-mid) tree[p].rnum+=tree[p*2].rnum;
}
else tree[p].num=tree[p].lnum=tree[p].rnum=1;
}
return;
}
if (b<=mid) sub(p*2,l,mid,a,b);
else if (a>mid) sub(p*2+1,mid+1,r,a,b);
else{
sub(p*2,l,mid,a,mid);
sub(p*2+1,mid+1,r,mid+1,b);
}
if (!tree[p].add){
tree[p].num=max(tree[p*2].rnum+tree[p*2+1].lnum,max(tree[p*2].num,tree[p*2+1].num));
tree[p].lnum=tree[p*2].lnum;
if (tree[p*2].lnum==mid-l+1) tree[p].lnum+=tree[p*2+1].lnum;
tree[p].rnum=tree[p*2+1].rnum;
if (tree[p*2+1].rnum==r-mid) tree[p].rnum+=tree[p*2].rnum;
}
}
int main(){
scanf("%d%d%d",&n,&m,&p);
fo(i,1,p){
scanf("%d%d%d%d",&a[i].x1,&a[i].y1,&a[i].x2,&a[i].y2);
inse(a[i].x1,i);
inse(a[i].x2,i);
}
build(1,1,m);
l=r=1;
t=h[1];
while (t){
add(1,1,m,a[go[t]].y1,a[go[t]].y2);
t=next[t];
}
while (r<=n){
ms=tree[1].num;
if (r-l+1>ms){
l++;
t=h[l-1];
while (t){
if (a[go[t]].x2==l-1) sub(1,1,m,a[go[t]].y1,a[go[t]].y2);
t=next[t];
}
}
else{
ans=max(ans,r-l+1);
r++;
t=h[r];
while (t){
if (a[go[t]].x1==r) add(1,1,m,a[go[t]].y1,a[go[t]].y2);
t=next[t];
}
}
}
printf("%d\n",ans);
}