【BOI2012】Mobile

    xiaoxiao2021-03-25  95

    Description

    平面上有一条线段,从(0,0)到(L,0) 有N个点,求线段上任意一点到这N个点中最近点的距离的最大值 换言之,求一个半径,使得以这N个点为圆心所成的圆能覆盖整条线段 1<=N<=10^6,1<=L<=10^9 输入的N个点按照x坐标不下降排列。如果两个点的x坐标相同,那么它们之间按照y坐标的升序排列。

    Analysis

    显然时间要求线性 假设所有n个点x坐标均不相同。如果有两个点x坐标相同,那么显然只需要保留y坐标绝对值较小的一个即可。这样可以少掉一些特判。 对于两个点,其中垂线左侧的点到左边的点更近,反之亦然 按x坐标的顺序从小到大扫描n个整点,用一个栈保存可能成为最近点的整点。同时维护栈中两点连线段中垂线与给出线段交点x坐标单调递增。每当加入一个点前,判断是否破坏了栈的单调性,如果不满足单调性则弹出栈顶元素直到单调性被满足,再将新点加入栈顶。最后答案即为线段的端点以及所有中垂线交点与他们最近点的距离的最大值。 证明的话可以自己手玩,看看如果单调性不满足会怎样

    Code

    #include<cstdio> #include<cmath> #include<algorithm> #define fo(i,a,b) for(int i=a;i<=b;i++) using namespace std; typedef double db; const db eps=1e-5; const int N=1000005; struct node { db x,y; }a[N],b[N]; int n,m,top,q[N]; db L,ans; db sqr(db x){return x*x;} db dis(db x1,db y1,db x2,db y2) { return sqrt(sqr(x1-x2)+sqr(y1-y2)); } db calc(node a,node b) { node c; c.x=(a.x+b.x)/2,c.y=(a.y+b.y)/2; return (a.y-c.y)*c.y/(a.x-c.x)+c.x; } int main() { freopen("mobile.in","r",stdin); freopen("mobile.out","w",stdout); db x=1e18,y=1e18; scanf("%d %lf",&m,&L); fo(i,1,m) { scanf("%lf %lf",&b[i].x,&b[i].y); x=min(x,dis(0,0,b[i].x,b[i].y)); y=min(y,dis(L,0,b[i].x,b[i].y)); } ans=max(x,y); b[m+1].x=1e18,x=b[1].y; fo(i,2,m+1) if(b[i].x!=b[i-1].x) a[++n].x=b[i-1].x,a[n].y=x,x=b[i].y; else if(abs(b[i].y)<abs(x)) x=b[i].y; q[top=1]=1; fo(i,2,n) { while(top>1 && calc(a[q[top]],a[i])<calc(a[q[top-1]],a[q[top]])) top--; if(top==1 && calc(a[q[top]],a[i])<0) top--; if(calc(a[q[top]],a[i])>L) continue; q[++top]=i; } while(top>1) { db pos=calc(a[q[top-1]],a[q[top]]); ans=max(ans,dis(pos,0,a[q[top]].x,a[q[top]].y)); top--; } printf("%.3lf",ans); return 0; }
    转载请注明原文地址: https://ju.6miu.com/read-23987.html

    最新回复(0)