[ARC 063 F]Snuke's Coloring 2

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;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值