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();
}
···
本文来自博客园,作者:liyixin,转载请注明原文链接:https://www.cnblogs.com/liyixin0514/p/18558661