20200730 T3 小B的农场【最大周长空矩形 (单调栈+线段树)】 && 「ROI 2017 Day 2」学习轨迹

小B的农场

题目描述

在这里插入图片描述
n ≤ 3 ∗ 1 0 5 n\le3*10^5 n3105

题目分析

n 2 log ⁡ n n^2\log n n2logn 的做法是枚举左边界,右边界从大到小枚举,维护中点 y y y 轴相邻两点差的最大值,删掉一个点时加入它上下相邻两点的差,双向链表实现。

n log ⁡ n n\log n nlogn 做法:
因为是整点,答案至少为 2 ∗ ( max ⁡ ( W , H ) + 1 ) 2*(\max(W,H)+1) 2(max(W,H)+1),所以答案矩形一定经过 x = W 2 x=\frac W2 x=2W y = H 2 y=\frac H2 y=2H 其中一条直线。(否则只能框在四分之一区域内)

假设经过 x = W 2 x=\frac W2 x=2W 这条线,将点按 y y y 从小到大排序,枚举矩形的上边界,维护每个矩形下边界对应的最宽宽度,越往下显然越窄,可以用单调栈+线段树区间加维护。

实现上,左右分别需要一个单调栈,每个左边的点会对它下面的下边界框出一个左边界(不包括自己),线段树的初值是每个点的 − y -y y,加上附加的宽度后取 max,再与当前的上边界 y y y 相加即为答案。
y y y 相等的几个点并不需要特殊处理,与下面的答案会在第一个点统计到,其它点正常框即可。

Code:

#include<bits/stdc++.h>
#define maxn 300005
using namespace std;
int n,W,H,ans;
struct node{
	int x,y;
	bool operator < (const node &p)const{return y<p.y;}
}a[maxn];
#define lc i<<1
#define rc i<<1|1
int mx[maxn<<2],tag[maxn<<2];
void build(int i,int l,int r){
	tag[i]=0;
	if(l==r) return void(mx[i]=W-a[l].y);
	int mid=(l+r)>>1;
	build(lc,l,mid),build(rc,mid+1,r);
	mx[i]=max(mx[lc],mx[rc]);
}
void ins(int i,int l,int r,int x,int y,int v){
	if(x>r||y<l) return;
	if(x<=l&&r<=y) {mx[i]+=v,tag[i]+=v;return;}
	int mid=(l+r)>>1;
	ins(lc,l,mid,x,y,v),ins(rc,mid+1,r,x,y,v);
	mx[i]=max(mx[lc],mx[rc])+tag[i];
}
int A[maxn],An,B[maxn],Bn;
void solve(){
	sort(a+1,a+1+n);
	build(1,0,n-1),An=Bn=0;
	for(int i=1;i<=n;i++){
		ans=max(ans,a[i].y+mx[1]);
		if(a[i].x<=W/2){
			ins(1,0,n-1,A[An],i-1,-a[i].x);
			while(An&&a[A[An]].x<a[i].x) ins(1,0,n-1,A[An-1],A[An]-1,-a[i].x+a[A[An]].x),An--;
			A[++An]=i;
		}
		else{
			ins(1,0,n-1,B[Bn],i-1,a[i].x-W);
			while(Bn&&a[B[Bn]].x>a[i].x) ins(1,0,n-1,B[Bn-1],B[Bn]-1,a[i].x-a[B[Bn]].x),Bn--;
			B[++Bn]=i;
		}
	}
}
int main()
{
	//freopen("farm.in","r",stdin);
	//freopen("farm.out","w",stdout);
	scanf("%d%d%d",&W,&H,&n);
	for(int i=1;i<=n;i++) scanf("%d%d",&a[i].x,&a[i].y);
	a[++n]=(node){W,H};
	solve();
	for(int i=1;i<=n;i++) swap(a[i].x,a[i].y);
	swap(W,H),solve();
	printf("%d\n",ans*2);
}

LOJ#2773. 「ROI 2017 Day 2」学习轨迹

在这里插入图片描述
在这里插入图片描述

Code:

#include<bits/stdc++.h>
#define maxn 500005
#define LL long long
using namespace std;
char cb[1<<20],*cs,*ct;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<20,stdin),cs==ct)?0:*cs++)
template<class T>inline void read(T &a){
	char c;while(!isdigit(c=getc()));
	for(a=c-'0';isdigit(c=getc());a=a*10+c-'0');
}
int n,m,a[maxn],b[maxn],c[maxn*2],p[maxn],pn,al,ar,bl,br,pos[maxn],A[maxn],An,B[maxn],Bn;//A[0],B[0] are used as 0!
LL ans,x[maxn],y[maxn];
LL mx[maxn<<2],tag[maxn<<2];
void build(int i,int l,int r,LL x,LL *y){
	tag[i]=0;
	if(l==r) return void(mx[i]=x-y[p[l]]);
	int mid=(l+r)>>1;
	build(i<<1,l,mid,x,y),build(i<<1|1,mid+1,r,x,y);
	mx[i]=max(mx[i<<1],mx[i<<1|1]);
}
void mdf(int i,int l,int r,int x,int y,LL v){
	if(x<=l&&r<=y) {mx[i]+=v,tag[i]+=v;return;}
	int mid=(l+r)>>1;
	if(x<=mid) mdf(i<<1,l,mid,x,y,v);
	if(y>mid) mdf(i<<1|1,mid+1,r,x,y,v);
	mx[i]=max(mx[i<<1],mx[i<<1|1])+tag[i];
}
int find(int i,int l,int r){
	if(l==r) return l;
	int mid=(l+r)>>1;
	if(mx[i]==mx[i<<1]+tag[i]) return find(i<<1,l,mid);
	else return find(i<<1|1,mid+1,r);
}
inline void updans(LL x,int a,int b,int c,int d,bool flg){
	if(flg) swap(a,c),swap(b,d);
	if(x>ans) ans=x,al=a,ar=b,bl=c,br=d;
}
void solve(int n,int m,int *a,int *b,LL *x,LL *y,bool flg){
	int mid=lower_bound(x+1,x+1+n,x[n]>>1)-x; pn=0;
	for(int i=1;i<=m;i++) if(b[i]) p[++pn]=i,pos[pn]=b[i]; p[pn+1]=m+1;
	build(1,0,pn,x[n],y);
	for(int i=An=Bn=0;i<=pn;i++){
		if(i){
			if(pos[i]<=mid){
				mdf(1,0,pn,A[An],i-1,-x[pos[i]]);
				while(An&&pos[i]>pos[A[An]]) mdf(1,0,pn,A[An-1],A[An]-1,-x[pos[i]]+x[pos[A[An]]]),--An;
				A[++An]=i;
			}
			else{
				mdf(1,0,pn,B[Bn],i-1,-x[n]+x[pos[i]-1]);
				while(Bn&&pos[i]<pos[B[Bn]]) mdf(1,0,pn,B[Bn-1],B[Bn]-1,-x[pos[B[Bn]]-1]+x[pos[i]-1]),--Bn;
				B[++Bn]=i;
			}
		}
		if(y[p[i+1]-1]+mx[1]>ans){
			int k=find(1,0,pn);
			int l=upper_bound(A+1,A+1+An,k)-A; l = l<=An?b[p[A[l]]]+1:1;
			int r=upper_bound(B+1,B+1+Bn,k)-B; r = r<=Bn?b[p[B[r]]]-1:n;
			updans(y[p[i+1]-1]+mx[1],l,r,p[k]+1,p[i+1]-1,flg);
		}
	}
}
int main()
{
	read(n),read(m);
	for(int i=1;i<=n;i++) read(a[i]);
	for(int i=1;i<=n;i++) read(x[i]),x[i]+=x[i-1];
	for(int i=1;i<=m;i++) read(b[i]);
	for(int i=1;i<=m;i++) read(y[i]),y[i]+=y[i-1];
	updans(x[n],1,n,0,0,0),updans(y[m],0,0,1,m,0);
	for(int i=1;i<=m;i++) c[b[i]]=i,b[i]=0;
	for(int i=1;i<=n;i++) a[i]=c[a[i]],b[a[i]]=i;
	solve(n,m,a,b,x,y,0),solve(m,n,b,a,y,x,1);
	printf("%lld\n%d %d\n%d %d\n",ans,al,ar,bl,br);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值