【校内模拟】小B的农场(单调栈)(线段树)

简要题意:

你有一个 W × H W \times H W×H 的矩形,中间一些位置上有树。请你求一个空矩形(允许边界有树),最大化周长。

W , H ≤ 1 e 8 W,H\leq 1e8 W,H1e8,树的个数 ≤ 3 e 5 \leq 3e5 3e5


题解:

容易注意到答案至少是 2 × max ⁡ ( W , H ) + 2 2\times \max(W,H)+2 2×max(W,H)+2。因为不管树怎么排,宽度为 1 1 1 的矩形永远都是可行的。于是容易注意到答案矩形必然穿过横向的中线或纵向的中线。

直接单调栈维护一下从中线向两边看到的情况,发现视角在向下移的时候对应栈中的区间加,同时需要维护一下坐标差的最大值,随便用一个数据结构即可。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

namespace IO{
	inline char gc(){
		static cs int Rlen=1<<22|1;static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}template<typename T>T get_integer(){
		char c;bool f=false;while(!isdigit(c=gc()))f=c=='-';T x=c^48;
		while(isdigit(c=gc()))x=((x+(x<<2))<<1)+(c^48);return f?-x:x;
	}inline int gi(){return get_integer<int>();}
}using namespace IO;

using std::cerr;
using std::cout;

cs int N=3e5+7;

int n,W,H,ans;

struct pt{int x,y;}p[N];

namespace SGT{

cs int N=::N<<2|7;

int mx[N],tg[N];
#define lc u<<1
#define rc lc|1
void build(int u,int l,int r){
	tg[u]=0,mx[u]=-p[l].y;if(l==r)return ;
	int mid=(l+r)>>1;build(lc,l,mid);build(rc,mid+1,r);
}

void add(int u,int l,int r,int ql,int qr,int vl){
	if(qr<l||r<ql)return ;
	if(ql<=l&&r<=qr)
		{tg[u]+=vl,mx[u]+=vl;return;}
	int mid=(l+r)>>1;
	if(ql<=mid)add(lc,l,mid,ql,qr,vl);
	if(mid<qr)add(rc,mid+1,r,ql,qr,vl);
	mx[u]=std::max(mx[lc],mx[rc])+tg[u];
}

inline void add(int l,int r,int vl){add(1,1,n,l,r,vl);}
#undef lc
#undef rc
}

int s1[N],t1,s2[N],t2;

void calc(){
	std::sort(p+1,p+n+1,[](cs pt &a,cs pt &b){
		return a.y<b.y||(a.y==b.y&&a.x<b.x);
	});SGT::build(1,1,n);t1=t2=0;
	for(int re i=1;i<=n;++i){
		if(i>1)SGT::add(i-1,i-1,W);
		ans=std::max(ans,(p[i].y+SGT::mx[1]));
		if(p[i].x<=W/2){
			SGT::add(s1[t1],i-1,-p[i].x);
			while(t1&&p[s1[t1]].x<p[i].x)
				{SGT::add(s1[t1-1],s1[t1]-1,p[s1[t1]].x-p[i].x);--t1;}
			s1[++t1]=i;
		}else {
			SGT::add(s2[t2],i-1,p[i].x-W);
			while(t2&&p[s2[t2]].x>p[i].x)
				{SGT::add(s2[t2-1],s2[t2]-1,p[i].x-p[s2[t2]].x);--t2;}
			s2[++t2]=i;
		}
	}
}

void Main(){
	W=gi(),H=gi(),n=gi();
	for(int re i=1;i<=n;++i)p[i].x=gi(),p[i].y=gi();
	p[++n]={0,0};p[++n]={W,H};calc();
	for(int re i=1;i<=n;++i)std::swap(p[i].x,p[i].y);
	std::swap(W,H);calc();cout<<ans*2<<"\n"; 
}

void file(){
#ifdef zxyoi
	freopen("farm.in","r",stdin);
#endif
}
signed main(){file();Main();return 0;}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值