AmazingCounters.com

[9018]1630: 离大海最远点在哪里?

9018传说级神题,精度要求只有,嗯,17位……跟老师交流后加上了SPJ,绝对误差小于1e-6或相对误差小于1e-8可过,总算是道能做的题了。

 

题目大意:给定一个n个点的凸多边形,求形内一点到边上最小距离最大,求出这个距离。(n<=200,000,坐标绝对值<=10^11,保留6位小数)

思路:容易想到二分答案,把边向内推然后半平面交check,复杂度O(nlogAns),看上去很科学实际上常数巨大无比,以至于在POJ上这题的n是100,而且这么做精度差也比较大。正解是用双向链表+堆维护这些边,模拟把边向内推进,先把所有边放进双向链表中,我们维护一条边什么时候会被相邻的两条边取代,容易发现是这条边到他与相邻两边的角平分线的交点的距离,用堆维护这个距离,每次把最小的那条边删掉,直到删到只剩两条边即可知道答案,复杂度O(nlogn)。

#include<cstdio>
#include<cmath>
#include<queue>
using namespace std;
#define ll long long
#define lb long double
inline ll read()
{
    ll x,f=1;char c;
    while((c=getchar())<'0'||c>'9')if(c=='-')f=0;
    for(x=c-'0';(c=getchar())>='0'&&c<='9';)x=(x<<3)+(x<<1)+c-'0';
    return f?x:-x;
}
#define MN 200000
struct point{lb x,y;point(lb x=0,lb y=0):x(x),y(y){}}p[MN+5];
point operator+(point a,point b){return point(a.x+b.x,a.y+b.y);}
point operator-(point a,point b){return point(a.x-b.x,a.y-b.y);}
point operator*(point a,lb b){return point(a.x*b,a.y*b);}
lb operator*(point a,point b){return a.x*b.y-a.y*b.x;}
lb length(point p){return sqrt(p.x*p.x+p.y*p.y);}
struct line{point p,v;}l[MN+5];
point operator*(line a,line b){return a.p+a.v*(b.v*(a.p-b.p)/(a.v*b.v));}
line ab(line a,line b){return (line){a*b,b.v*length(a.v)-a.v*length(b.v)};}
priority_queue<pair<lb,int> > pq;
int ls[MN+5],nx[MN+5],u[MN+5];
lb cal(int x){return -fabs(l[x].v*(ab(l[ls[x]],l[x])*ab(l[x],l[nx[x]])-l[x].p)/length(l[x].v));}
int main()
{
    int n=read(),i,j;
    for(i=0;i<n;++i)p[i].x=read(),p[i].y=read();
    for(p[n]=p[i=0];i<n;++i)l[i].p=p[i],l[i].v=p[i+1]-p[i];
    for(i=0;i<n;++i)ls[i]=i-1,nx[i]=i+1;nx[ls[0]=n-1]=0;
    for(i=0;i<n;++i)pq.push(make_pair(cal(i),i));
    for(i=3;i<n;++i)
    {
        for(u[j=pq.top().second]=1;u[pq.top().second];)pq.pop();
        nx[ls[j]]=nx[j];ls[nx[j]]=ls[j];
        pq.push(make_pair(cal(ls[j]),ls[j]));
        pq.push(make_pair(cal(nx[j]),nx[j]));
    }
    printf("%.6lf",-(double)pq.top().first);
}

 

posted on 2017-03-17 10:02  ditoly  阅读(162)  评论(0编辑  收藏  举报