SS241120D. 周长(perimeter)

SS241120D. 周长(perimeter)

题意

简化一下题意,就是给你 \(n\) 个点,求最大的矩形,满足矩形内部没有严格包含的点,问面积是多少。

思路

\(O(n^2)\) 算法

\(O(n^3)\) 是显然的,枚举上边界、下边界、左边界,然后求出最大合法右边界即可,可以在枚举的时候维护严格在矩形内部的点的最小横坐标,这个就是右边界了。

我们可以枚举上、下边界,确定矩形的高,然后发现合法的矩形的长就是严格在上下边界之内的点,按照横坐标排序,对横坐标求差分数组,因此取差分数组的 \(\max\) 就好。

\(\max\) 的话考虑调整枚举上、下边界的顺序,使得总是删点(差分数组总是变大)而不会出现加点(差分数组变小)的情况。可以顺序枚举上边界,倒序枚举下边界。使用链来维护差分数组。这样就是 \(O(n^2)\) 了。

正解

正解是 \(O(n \log n)\),但是小常数双 \(\log\) 也可以干过去。

我们以上方为 \(y\) 轴正方向,右方为 \(x\) 轴正方向建立平面直角坐标系。

对于这种扫描线问题,考虑分治。我们分治 \(x\) 坐标,钦定一个直线 \(x=mid\),要求矩形经过这条直线。如果不经过这条直线的就分治左右两边来做。

对于一次分治的问题,考虑扫描线下边界,维护最优的上边界。

发现最优决策没有什么单调的性质,考虑使用数据结构维护。

周长等于二倍长加宽,二倍可以不管。因此 \(ans = (r-l+up-down)\)。其中 \(down\) 是确定的,而 \(l,r\) 容易根据 \(up\) 来唯一确定,因为我们强制矩形经过 \(x=mid\) 了。

以下忽略离散化的过程。

\(f_i\) 表示 \(up=i\) 的时候 \(r-l+i\) 是多少。那么 \(ans = f_i - down\)

刚开始 \(f_m = n + m\)。考虑加入第 \(i\) 行的点造成什么影响。

\(f_i := n + i\),对上面的行,假设加入的点的 \(x\) 坐标 \(>mid\),那么所有的 \(r \ge x\) 的行,都要使 \(r\gets x\)。发现这是一个颜色段均摊的问题,我们可以对一样的 \(r\) 做区间加(区间减是一样的),然后合并颜色段。可以使用栈维护。对于 \(x<mid\) 同理。(可以画图理解)

线段树维护区间最大值,询问最大的 \(f_i\)

这样就是 \(O(n \log^2 n)\) 的了。

然后你惊奇地发现答案下界其实是 \(2\max(n,m)\),构造方式是你选随便一个点,把它左边和右边全部删掉,或者把它上边和下边全部删掉。

因此你的最优矩形一定会经过 \(x=\frac{n}{2}\)\(y=\frac{m}{2}\) 至少其一,否则周长小于下界。

因此你的分治只需要进行一次。非常巧妙神奇有趣幽默可爱活泼。

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

code

感觉码量会比较大?不知道要写多久。

祭,调不出来,已疯。

2024-11-21 21:56:46 星期四:终于调好了,真难调,码力有待提高。

#include<bits/stdc++.h>
#define sf scanf
#define pf printf
#define rep(x,y,z) for(int x=y;x<=z;x++)
#define per(x,y,z) for(int x=y;x>=z;x--)
typedef long long ll;
using namespace std;
namespace Luminous { 
    constexpr int N=3e5+7;
    int n,m,s;
    struct node {
        int x,y;
    }a[N],b[N];
    int ans;
    struct seg {
        int tr[N<<2],tag[N<<2];
        void clear() { memset(tr,0,sizeof(tr)), memset(tag,0,sizeof(tag)); };
        void maketag(int u,int val) { tr[u]+=val, tag[u]+=val; }
        void pushup(int u) { tr[u]=max(tr[u<<1],tr[u<<1|1]); }
        void pushdown(int u) { if(tag[u]) maketag(u<<1,tag[u]), maketag(u<<1|1,tag[u]), tag[u]=0; }
        void change(int u,int l,int r,int x,int val) {
            if(l==r) return tr[u]+=val, void(0);
            int mid=(l+r)>>1;
            pushdown(u);
            if(x<=mid) change(u<<1,l,mid,x,val);
            else change(u<<1|1,mid+1,r,x,val);
            pushup(u);
        }
        void change(int u,int l,int r,int L,int R,int val) {
            if(l>=L && r<=R) return maketag(u,val), void(0);
            int mid=(l+r)>>1;
            pushdown(u);
            if(L<=mid) change(u<<1,l,mid,L,R,val);
            if(mid+1<=R) change(u<<1|1,mid+1,r,L,R,val);
            pushup(u);
        }
        int ask() { return tr[1]; }
    }T; 
    int rky[N];
    bool cmpy(int p,int q) { return a[p].y > a[q].y; }
    void _max(int &a,int b) { a=max(a,b); }
    int stl[N],topl,str[N],topr;
    void addl(int i,int x) {
        T.change(1,0,s,i,-x);
        while(topl && b[stl[topl]].x <= x) {
            T.change(1,0,s,stl[topl-1]+1,stl[topl],b[stl[topl]].x-x);
            --topl;
        }
        stl[++topl]=i;
    }
    void addr(int i,int x) {
        T.change(1,0,s,i,x);
        while(topr && b[str[topr]].y >= x) {
            T.change(1,0,s,str[topr-1]+1,str[topr],x-b[str[topr]].y);
            --topr;
        }
        str[++topr]=i;
    }
    void solve() {
        topl=topr=0;
        stl[0]=str[0]=-1;
        rep(i,1,s) rky[i]=i;
        sort(rky+1,rky+s+1,cmpy);
        a[0]={n,m};
        rep(i,1,s) _max(ans,n-1+a[rky[i-1]].y-a[rky[i]].y);
        _max(ans,n-1+a[rky[s]].y-1);
        rep(i,0,s) {
            int id=rky[i];
            node u=a[id];
            _max(ans,T.ask()-u.y);
            if(u.x <= n>>1) {
                b[i]={u.x,n};
                addl(i,u.x), addr(i,n);
            }else {
                b[i]={1,u.x};
                addl(i,1), addr(i,u.x);
            }
            T.change(1,0,s,i,i==0 ? u.y : a[rky[i-1]].y);
        }
        _max(ans,T.ask()-1);
    }
    void zhuanquanquan() {
        swap(n,m);
        rep(i,0,s) swap(a[i].x,a[i].y);
        T.clear();
    }
    void main() {
        sf("%d%d%d",&s,&n,&m);
        ++n,++m;
        rep(i,1,s) sf("%d%d",&a[i].x,&a[i].y), ++a[i].x,++a[i].y;
        solve();
        zhuanquanquan();
        solve();
        pf("%d\n",ans<<1);
    }
}
int main() {
    #ifdef LOCAL
    freopen("my.out","w",stdout);
    #else 
    freopen("perimeter.in","r",stdin);
    freopen("perimeter.out","w",stdout);
    #endif
    Luminous :: main();
}
···
posted @ 2024-11-20 20:01  liyixin  阅读(6)  评论(0编辑  收藏  举报