Description
给出一个wxh的网格图,和n个点,求一个周长最大的矩形,满足这个矩形内部没有点。注意矩形边界上不算在内部。
n<=2*1e5
Solution
首先让我们来想一个分治做法。
分治了一条中线,我们想要求出跨过中线的答案。
那么对于中线上下两侧,我们做两个单调栈,用扫描线维护当前的右端点,然后左端点只能在单调栈上。
当然你不能暴力求上下单调栈是哪个,我们可以用线段树来维护每一段离中线的距离的和,然后是求最大值。操作直接在退栈的时候加加减减就好了。
这样的复杂度是O(n log^2 n),但是注意到答案的下界是2*(min(w,h)+1),于是我们只需要分治第一层就好了。
复杂度O(n log n)
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
typedef long long ll;
int read() {
char ch;
for(ch=getchar();ch<'0'||ch>'9';ch=getchar());
int x=ch-'0';
for(ch=getchar();ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
return x;
}
const int N=2*1e5+5;
int w,h,n,ta,tb,ans;
struct P{int x,y;}p[N],sta[N],stb[N];
bool cmp(P x,P y) {return x.x<y.x;}
int tr[N<<2],tag[N<<2];
void add(int v,int z) {tr[v]+=z;tag[v]+=z;}
void down(int v) {
if (tag[v]) {
add(v<<1,tag[v]);
add(v<<1|1,tag[v]);
tag[v]=0;
}
}
void modify(int v,int l,int r,int x,int y,int z) {
if (l==x&&r==y) {add(v,z);return;}
int mid=l+r>>1;down(v);
if (y<=mid) modify(v<<1,l,mid,x,y,z);
else if (x>mid) modify(v<<1|1,mid+1,r,x,y,z);
else modify(v<<1,l,mid,x,mid,z),modify(v<<1|1,mid+1,r,mid+1,y,z);
tr[v]=max(tr[v<<1],tr[v<<1|1]);
}
void solve() {
sort(p+1,p+n+1,cmp);
fo(i,1,n<<2) tr[i]=tag[i]=0;
ta=tb=0;
fo(i,1,n) {
if (p[i].y<=h/2) {
int la=i-1;
while (ta&&sta[ta].y<p[i].y) {
modify(1,1,n,sta[ta].x,la,sta[ta].y-p[i].y);
la=sta[ta--].x-1;
}
if (la!=i-1) sta[++ta].x=la+1,sta[ta].y=p[i].y;
} else {
int la=i-1;
while (tb&&stb[tb].y>p[i].y) {
modify(1,1,n,stb[tb].x,la,p[i].y-stb[tb].y);
la=stb[tb--].x-1;
}
if (la!=i-1) stb[++tb].x=la+1,stb[tb].y=p[i].y;
}
sta[++ta].x=i;sta[ta].y=0;
stb[++tb].x=i;stb[tb].y=h;
modify(1,1,n,i,i,h-p[i].x);
ans=max(ans,tr[1]+p[i+1].x);
}
}
int main() {
freopen("paint.in","r",stdin);
freopen("paint.out","w",stdout);
w=read();h=read();n=read();
fo(i,1,n) {
p[i].x=read();
p[i].y=read();
}
p[++n].x=0;p[n].y=0;
p[++n].x=w;p[n].y=h;
solve();
fo(i,1,n) swap(p[i].x,p[i].y);
swap(w,h);
solve();
printf("%d\n",ans*2);
return 0;
}