JZOJ 5627. 【NOI2018模拟4.3】paint

Description

Description

Input

Input

Output

Output

Sample Input

样例输入1

10 10 4
1 6
4 1
6 9
9 4

样例输入2

10 10 4
2 2
4 4
7 7
9 9

Sample Output

样例输出1

32

样例输出2

26

Data Constraint

Data Constraint

Solution

  • 先贴上原题题解:

Solution

  • 为方便统计答案,先加入两个边界点 (0,0) (w,h)

  • 之后将所有点按 X 坐标从小到大排序。

  • 我们对于直线 l:y=h2 ,上下同时维护各一个值远离逐渐 l 的单调栈。

  • 栈中存着许多线段,线段树中存着两个栈元素纵坐标的相反数 Y1Y2

  • 则取出后加上 h 就是对应矩形的宽了。

  • 其中 zjp_shadow的博客 讲的很详细:

Solution

  • (虚线 为中线,黑色 是当前单调栈里的,红色 是现在将过来的一个线段)

  • 我们将两个栈顶线段的答案进行更改,将这些线段的横着的答案变小它坐标的相应的差值。

  • 这个就可以直接在线段树上做加减法就行了。

  • 然后我们用这条 绿色红色 的线段一起,共同构成一个新的线段存进单调栈中去。

  • 为了同时得到矩形的长,我们把值在减去当前的横坐标 Xi

  • 之后在查询时加上目前的横坐标 Xi+1 ,这样就得出了对应矩形的长 Xi+1Xi

  • 这样也就得到了当前矩形一半周长的最优答案。

  • 注意每做完一个点 i 时,要及时更新答案,并将 i 点的信息单点加到线段树上。

  • 为了避免 i+1 不能成功进入栈中,需要在栈末尾加入一个极值点 (i,h/0) 来保证入栈。

  • 而对于直线 x=w2 也同理,交换坐标再做一遍即可。

  • 时间复杂度 O(N log N)

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
using namespace std;
const int N=2e5+5;
struct data
{
    int x,y;
}a[N],f[N<<2],st1[N]/*down*/,st2[N]/*up*/;
int w,h,n,ans;
int top1,top2,qx,qy,qz;
inline int read()
{
    int X=0,w=0; char ch=0;
    while(!isdigit(ch)) w|=ch=='-',ch=getchar();
    while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    return w?-X:X;
}
inline bool cmp(data x,data y)
{
    return x.x<y.x;
}
inline int max(int x,int y)
{
    return x>y?x:y;
}
void change(int v,int l,int r)
{
    if(qx<=l && r<=qy)
    {
        f[v].x+=qz;
        f[v].y+=qz;
        return;
    }
    int mid=l+r>>1;
    if(qx<=mid) change(v<<1,l,mid);
    if(qy>mid) change(v<<1|1,mid+1,r);
    f[v].x=max(f[v<<1].x,f[v<<1|1].x)+f[v].y;
}
inline void solve()
{
    memset(f,top1=top2=0,sizeof(f));
    sort(a+1,a+1+n,cmp);
    for(int i=1;i<n;i++)
    {
        if(a[i].y<=h>>1)
        {
            int t=i-1;
            while(top1 && st1[top1].y<a[i].y)
            {
                qx=st1[top1].x,qy=t,qz=st1[top1].y-a[i].y;
                change(1,1,n);
                t=st1[top1--].x-1;
            }
            if(t^i-1) st1[++top1]=(data){t+1,a[i].y};
        }else
        {
            int t=i-1;
            while(top2 && st2[top2].y>a[i].y)
            {
                qx=st2[top2].x,qy=t,qz=a[i].y-st2[top2].y;
                change(1,1,n);
                t=st2[top2--].x-1;
            }
            if(t^i-1) st2[++top2]=(data){t+1,a[i].y};
        }
        st1[++top1]=(data){i,0};
        st2[++top2]=(data){i,h};
        qx=qy=i,qz=h-a[i].x;
        change(1,1,n);
        ans=max(ans,f[1].x+a[i+1].x);
    }
}
int main()
{
    w=read(),h=read(),n=read();
    for(int i=1;i<=n;i++) a[i].x=read(),a[i].y=read();
    a[++n]=(data){0,0};
    a[++n]=(data){w,h};
    solve();
    for(int i=1;i<=n;i++) swap(a[i].x,a[i].y);
    swap(w,h);
    solve();
    printf("%d",ans<<1);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值