bzoj2146 Construct
题意
给出一个所有边都和坐标轴平行的多边形,要求画一个周长尽量小的且所有边也和坐标轴平行的多边形把这个多边形围起来.在周长尽量小的前提下,希望围起来的面积尽量小
点数<=1e5,坐标的绝对值<=1e9
分析
对于第一问:我们画的多边形必须到达给出的多边形的横纵坐标的最大和最小值.假设横纵坐标的最大最小值分别为minx,miny,maxx,maxy,那么周长至少为2(maxx-minx+maxy-miny),而且这个下界显然可以达到,画一个矩形即可.
实际上,maxx-minx+maxy-miny会炸int,所以printf("%lld\n",2ll(maxx-minx+maxy-miny)会WA.
智障选手就是我,我就是智障选手
那么第二问呢?我们知道一个大小为(maxx-minx)*(maxy-miny)的矩形基本不可能是最优解,脑补一下就发现我们可以使得一些边界向里收紧和给出的多边形贴紧,同时周长不变.例如给定一个汉字"凸"形状的多边形,最优解中画的边界紧贴给出的多边形的每一条边界.同时我们也发现有时画的边界不能和某些位置贴紧,例如给定一个汉字"凹"形状的多边形,中间凹下去的地方我们的边界是不能伸进去的(如果把边界画在里面,就不能保证周长最短).
考虑这样计算面积:把x坐标离散化,假设有tot个不同的x坐标,那么我们现在把整个图形切成了tot-1段.显然同一段内上边界是一条不会拐弯的水平直线,下边界也是一条不会拐弯的水平直线,这一段的面积用上下边界纵坐标之差*这一段的横向长度即可.现在我们只需要分别算出每一段的上下边界的高度.由于对称性,现在只考虑上边界.
对于某一段,我们求出这段区间内给定的多边形的最高的一段水平边界的高度.显然这里我们画出的上边界的位置不能比这个高度低,但不一定能够恰好贴着这个高度画.例如"凹".
接下来观察一下规律就好了:最高点一定可以贴着最高点的高度画,然后向两边依次降低.考虑向左边画的情况,如果给定的多边形的某一段边界再向左侧走会有一段更高的边界,那么这里贴着边界画就不合法,只有左边都不比这里高的时候才可以贴着这里画.
实际上,规律可以表示成这样:对于从左到右每一段区间Interval1,Interval2...我们求出每段区间里给定的多边形的最高一段水平边界的高度Height1,Height2...
对于第i段区间,我们求出前i段区间的Height的最大值preMax,求出第i段区间以及之后的区间的Height的最大值sufMax,第i段区间中我们画的边界的高度就是min(preMax,sufMax)
写两棵线段树就好了.
又丑又长常数飞起.
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=100005,inf=0x7f7f7f7f;
typedef long long ll;
int x[maxn],y[maxn];
int dict[maxn],seq[maxn];
bool cmp(const int &a,const int &b){
return x[a]<x[b];
}
void bemax(int &a,int b){
if(a<b)a=b;
}
void bemin(int &a,int b){
if(a>b)a=b;
}
int Min[maxn<<2],Max[maxn<<2];
int markmin[maxn<<2],markmax[maxn<<2];
int tot;
void pdmin(int rt){
bemin(Min[rt<<1],markmin[rt]);
bemin(Min[rt<<1|1],markmin[rt]);
bemin(markmin[rt<<1],markmin[rt]);
bemin(markmin[rt<<1|1],markmin[rt]);
}
void addmin(int rt,int l,int r,int ql,int qr,int x){
if(ql<=l&&r<=qr){
bemin(Min[rt],x);
bemin(markmin[rt],x);
return;
}
pdmin(rt);
int mid=(l+r)>>1;
if(ql<=mid)addmin(rt<<1,l,mid,ql,qr,x);
if(qr>mid) addmin(rt<<1|1,mid+1,r,ql,qr,x);
Min[rt]=min(Min[rt<<1],Min[rt<<1|1]);
}
void pdmax(int rt){
bemax(Max[rt<<1],markmax[rt]);
bemax(Max[rt<<1|1],markmax[rt]);
bemax(markmax[rt<<1],markmax[rt]);
bemax(markmax[rt<<1|1],markmax[rt]);
}
void addmax(int rt,int l,int r,int ql,int qr,int x){
if(ql<=l&&r<=qr){
bemax(Max[rt],x);
bemax(markmax[rt],x);
return;
}
pdmax(rt);
int mid=(l+r)>>1;
if(ql<=mid)addmax(rt<<1,l,mid,ql,qr,x);
if(qr>mid) addmax(rt<<1|1,mid+1,r,ql,qr,x);
Max[rt]=max(Max[rt<<1],Max[rt<<1|1]);
}
int qmax(int rt,int l,int r,int ql,int qr){
if(ql<=l&&r<=qr)return Max[rt];
pdmax(rt);
int ans=-inf,mid=(l+r)>>1;
if(ql<=mid)bemax(ans,qmax(rt<<1,l,mid,ql,qr));
if(qr>mid)bemax(ans,qmax(rt<<1|1,mid+1,r,ql,qr));
return ans;
}
int qmin(int rt,int l,int r,int ql,int qr){
if(ql<=l&&r<=qr)return Min[rt];
pdmin(rt);
int ans=inf,mid=(l+r)>>1;
if(ql<=mid)bemin(ans,qmin(rt<<1,l,mid,ql,qr));
if(qr>mid)bemin(ans,qmin(rt<<1|1,mid+1,r,ql,qr));
return ans;
}
void Add(int l,int r,int y){
if(l>r)swap(l,r);
r--;
addmin(1,1,tot-1,l,r,y);
addmax(1,1,tot-1,l,r,y);
}
int main(){
int n;scanf("%d",&n);
int minx=inf,maxx=-inf,miny=inf,maxy=-inf;
for(int i=1;i<=n;++i){
scanf("%d%d",x+i,y+i);
minx=min(minx,x[i]);miny=min(miny,y[i]);
maxx=max(maxx,x[i]);maxy=max(maxy,y[i]);
}
x[n+1]=x[1];y[n+1]=y[1];
printf("%lld\n",2ll*(maxx-minx)+2ll*(maxy-miny));
for(int i=1;i<=n;++i)seq[i]=i;
sort(seq+1,seq+n+1,cmp);
int old=inf;
tot=0;
for(int i=1;i<=n;++i){
if(old!=x[seq[i]]){
++tot;dict[tot]=x[seq[i]];old=x[seq[i]];
}
x[seq[i]]=tot;
}
x[n+1]=x[1];
memset(markmin,0x7f,sizeof(markmin));
memset(Min,0x7f,sizeof(Min));
memset(markmax,0xc2,sizeof(markmax));
memset(Max,0xc2,sizeof(Max));
for(int i=1;i<=n;++i){
if(x[i]!=x[i+1])Add(x[i],x[i+1],y[i]);
}
long long ans=0;
for(int i=1;i<tot;++i){
int upperbound=min(qmax(1,1,tot-1,1,i),qmax(1,1,tot-1,i,tot-1));
int lowerbound=max(qmin(1,1,tot-1,1,i),qmin(1,1,tot-1,i,tot-1));
ans=ans+(upperbound-lowerbound)*1ll*(dict[i+1]-dict[i]);
}
printf("%lld\n",ans);
return 0;
}