[ARC063] F - すぬけ君の塗り絵 2 / Snuke's Coloring 2 单调栈+线段树

首先发现一定能找到一个最优解使得矩形的每条边要么是边界,要么有一个关键点,所以转化为求周长最大的内部不存在关键点的矩形。
不难发现选择一竖条或者一横条是一定合法的,那么答案下界就至少是 2(max(h,w)+1) 2 ( max ( h , w ) + 1 ) ,那么最优解一定过 x=w2 x = w 2 或者 y=h2 y = h 2 ,因为在某个四分之一矩形内部是不可能达到答案下界的。
先考虑经过 x=w2 x = w 2 的答案(另一部分同理),那么枚举上边界 yL y L ,用两个单调栈维护下边界 yR y R 递增时的左右边界 xL,xR x L , x R ,可以 O(n2) O ( n 2 ) 求解。其实对于 yR y R 递增的过程,我们可以用线段树维护 yL=1..n y L = 1.. n 的最小周长,单调栈的操作相当于在线段树上区间加减,然后维护一个全局最小值即可。
代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define chkmin(a,b) a=min(a,b)
#define chkmax(a,b) a=max(a,b)
#define mid (l+r>>1)
#define N 200010
using namespace std;
int w,h,n,m,z[N],hl[N],hr[N],ans,topl,topr;
int read()
{
    int x=0,f=1;char ch=getchar();
    for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') f=-1;
    for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
    return x*f;
}
struct pt
{
    int x,y;
}a[N],sl[N],sr[N];
struct tree
{
    int add,mx;
    tree *ls,*rs;
    tree(){ls=rs=0;mx=add=0;}
    void update()
    {
        mx=max(ls->mx,rs->mx);
    }
    void cal(int d)
    {
        mx+=d;add+=d;
    }
    void pushdown()
    {
        ls->cal(add);rs->cal(add);
        add=0;  
    }
    void build(int l,int r)
    {
        if(l==r) return ;
        (ls=new tree)->build(l,mid);
        (rs=new tree)->build(mid+1,r);
    }
    void mdf(int l,int r,int lx,int rx,int d)
    {
        if(lx>rx||d==0) return ;
        if(l==lx&&r==rx) {cal(d);return ;}
        pushdown();
        if(rx<=mid) ls->mdf(l,mid,lx,rx,d);
        else if(lx>mid) rs->mdf(mid+1,r,lx,rx,d);
        else ls->mdf(l,mid,lx,mid,d),rs->mdf(mid+1,r,mid+1,rx,d);
        update();
    }
}*xtr;
void work()
{
    for(int i=1;i<=n;i++)
        z[i]=a[i].y;
    sort(z,z+n+1);z[n+1]=h;
    m=unique(z,z+n+2)-z-1;
    for(int i=1;i<=n;i++)
        a[i].y=lower_bound(z,z+m+1,a[i].y)-z;
    (xtr=new tree)->build(0,m); 
    for(int i=0;i<=m;i++)
        hl[i]=0,hr[i]=w;    
    for(int i=1;i<=n;i++)
        if(a[i].x<=(w>>1)) chkmax(hl[a[i].y],a[i].x);
        else chkmin(hr[a[i].y],a[i].x);         
    topl=topr=0;
    xtr->mdf(0,m,0,m,w);
    for(int i=1;i<=m;i++)
    {
        xtr->mdf(0,m,0,i-1,z[i]-z[i-1]);
        chkmax(ans,xtr->mx);
        for(;topl&&hl[i]>=sl[topl].x;topl--)
            xtr->mdf(0,m,sl[topl-1].y,sl[topl].y-1,sl[topl].x-hl[i]);   
        for(;topr&&hr[i]<=sr[topr].x;topr--)
            xtr->mdf(0,m,sr[topr-1].y,sr[topr].y-1,hr[i]-sr[topr].x);
        xtr->mdf(0,m,i-1,i-1,hr[i]-hl[i]-w);
        sl[++topl]=(pt){hl[i],i};sr[++topr]=(pt){hr[i],i};

    }   
}
int main()
{
    w=read();h=read();n=read();
    for(int i=1;i<=n;i++)
        a[i].x=read(),a[i].y=read();
    work();
    for(int i=1;i<=n;i++)
        a[i].y=z[a[i].y];
    swap(w,h);
    for(int i=1;i<=n;i++)
        swap(a[i].x,a[i].y);    
    work();
    printf("%d",ans<<1);            
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值