Loading

题解-ARC063D Snuke's Coloring 2

题面

ARC063D Snuke's Coloring 2

给一个 \(W\times H\) 的矩形和 \(n\) 个点 \((x_i,y_i)\)。对于每个点,都必须把它四个方向之一的区域全涂黑。最后留下一个白色矩形,求它的周长最大值。

数据范围:\(1\le W,H\le 10^8\)\(1\le N\le 3\times 10^5\)


题解

题目就是要求一个周长最大的矩形,在给定矩形内,且不包含给定的点。

答案至少是 \(2\max(W,H)\),把矩形选成长条形的即可。

所以 \(x,y\) 中至少有一维,答案矩形越过给定矩形中线。


假设越过 \(y\) 轴中线(否则同理)。

对给定的点按 \(x\) 轴排序。枚举矩形的右端点在第几个点,设为 \(i+1\)

如果当前的点 \(i\) 满足 \(y_i<\frac H2\),那么这个点可以看成限制区间 \([y_i,H]\)

否则可以看成限制区间 \([0,y_i]\)

用一棵线段树维护当左端点在第 \(j\) 个点时的 \(ans-x_{i+1}\) 最大值。

线段树支持区间加减和全局最大值。

所以可以维护两个年轻栈,一个在 \(\frac H2\) 下端,一个在 \(\frac H2\) 上端。

栈中的元素是之前的点,用二元组 \((j,y)\) 表示。

表示从 \(j\)\(i+1\) 矩阵的上边最大/下边最小为 \(y\)


假设 \(y_i<\frac H2\)(否则同理),就用 \(y_i\) 把下端栈的栈顶 \(<y_i\)pop 掉。

pop 一次,象征着左端点在被 pop 掉的区域的下边被抬升了,在线段树上修改。

在最后被 pop 的地方 \(r\) 之右,这些地方作为左端点的下边都是 \(y_i\)

所以往下端栈中加入元素 \((r,y_i)\)


假设第 \(i\) 个点和 \(i+1\) 个点中有个虚拟点在最顶上/底下。

所以在 \(i\) 这个地方的线段树上单点修改 \(+H\)

还要 \(-x_i\),为了到时候可以计算横向区间长度。

同时在上端栈加入 \((i,H)\),下端加入 \((i,0)\)

然后统计右端点在 \(i+1\) 点的贡献:线段树全局最大值 \(+x_{i+1}\)


最后要输出求出来的答案 \(\times 2\)

时间复杂度 \(\Theta(n\log n)\)


代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define x first
#define y second
#define bg begin()
#define ed end()
#define pb push_back
#define mp make_pair
#define sz(a) int((a).size())
#define R(i,n) for(int i(0);i<(n);++i)
#define L(i,n) for(int i((n)-1);i>=0;--i)
const int iinf=0x3f3f3f3f;
const ll linf=0x3f3f3f3f3f3f3f3f;

//Data
const int N=3e5+4;
int n,W,H,ns;
stack<pair<int,int>> us,ds;
pair<int,int> a[N];

//SegmentTree
const int sN=N<<2;
#define mid ((l+r)>>1)
int mx[sN],tag[sN];
void pushup(int u){mx[u]=max(mx[u*2+1],mx[u*2+2]);}
void down(int u,int v){mx[u]+=v,tag[u]+=v;}
void pushdown(int u){if(tag[u]) down(u*2+1,
    tag[u]),down(u*2+2,tag[u]),tag[u]=0;}
void add(int x,int y,int v,int u=0,int l=0,int r=n){
    // if(u==0) cout<<x<<' '<<y<<' '<<v<<'\n';
    if(y<=l||r<=x) return;
    if(x<=l&&r<=y) return down(u,v);
    pushdown(u),add(x,y,v,u*2+1,l,mid),
    add(x,y,v,u*2+2,mid,r),pushup(u);
}
#undef mid

//Function
void solve(){
    // cout<<"solving\n";
    sort(a,a+n);
    R(i,n-1){
        if(a[i].y<(H>>1)){
            int r=i;
            while(sz(ds)&&ds.top().y<a[i].y){
                const int l=ds.top().x;
                add(l,r,ds.top().y-a[i].y),r=l,ds.pop();
            }
            ds.push(mp(r,a[i].y));
        } else {
            int r=i;
            while(sz(us)&&us.top().y>a[i].y){
                const int l=us.top().x;
                add(l,r,a[i].y-us.top().y),r=l,us.pop();
            }
            us.push(mp(r,a[i].y));
        }
        us.push(mp(i,H)),ds.push(mp(i,0));
        add(i,i+1,H-a[i].x),ns=max(ns,mx[0]+a[i+1].x);
    }
    while(sz(ds)) ds.pop();
    while(sz(us)) us.pop();
    R(u,n<<2) mx[u]=tag[u]=0;
}

//Main
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cin>>W>>H>>n; //,ns=max(W,H);
    R(i,n) cin>>a[i].x>>a[i].y;
    a[n++]=mp(0,0),a[n++]=mp(W,H);
    solve(),swap(W,H);
    R(i,n) swap(a[i].x,a[i].y);
    solve(),cout<<(ns<<1)<<'\n';
    return 0;
}

祝大家学习愉快!

posted @ 2020-12-22 16:55  George1123  阅读(202)  评论(1编辑  收藏  举报