[BZOJ2033][清橙A1215][2009国家集训队]大灾变-半平面交

大灾变

Description

  艾泽拉斯世界经历一场亘古未有的地震过后,大地和海洋被完全撕裂,旧大陆残缺不全。联盟和部落各种族的居民们被迫离开了世代居住的家园,来寻找新的生存空间。原本平坦的陆地上现在隆起了一座座山峰,暴风城的人类开始在艾尔文山脉重建家园。他们决定在山脉之中建造一座瞭望塔和一个魔法浮空岛,以便于在瞭望塔和浮空岛上可以俯视艾尔文山脉的全貌。
  艾尔文山脉被描述为一个折线,给定每个点的坐标(横纵坐标均不小于0),按照横坐标从小到大顺次连接起来就是就是山脉的折线。折线上所有点的横坐标均不相同。如果一个位置与山脉任何一点的连线均不被挡住(但可以与地面相切),那么就说这一点可以望到整个艾尔文山脉。瞭望塔的塔身不会挡住视线,而且瞭望塔和浮空岛可以建造在同一位置。为节省建筑材料,瞭望塔塔身的高度必须尽量小,即从塔顶到塔底的距离尽量小,瞭望塔可以建在山坡上。由于气候因素,浮空岛应建立在海拔尽量低的位置(甚至可以建在地面上),海平面高度为0。如果有多个位置均满足条件,则选择横坐标最小的那个。瞭望塔和浮空岛横坐标范围应在艾尔文山脉横坐标范围之内。给定艾尔文山脉,请你求出瞭望塔和浮空岛的位置。

Input

  第1行,一个整数N,表示描述艾尔文山脉的折线的顶点数。
  第2-N+1行,每行两个整数,xi,yi表示折线上点的坐标。

Output

  第1行,两个保留3位小数的浮点数x1,y1,表示瞭望塔顶端的坐标。
  第2行,两个保留3位小数的浮点数x2,y2,表示浮空岛的坐标。

Sample Input

3
2 0
1 10
3 10

Sample Output

1.00 10.00
2.00 0.00

HINT

  样例中描述的艾尔文山脉各个顶点,按照横坐标顺序顺次连接后的折线如下图所示:
  pic1
  瞭望塔应建造在山峰(8,6)处,塔顶端为(8,11),高度为5,此时瞭望塔的高度最小。
  pic2
  浮空岛建立在(9.54,9.85)处,海拔高度最低。
  pic3

  40%的数据2<=N<=10
  100%的数据2<=N<=1 000 000;0<=xi,yi<=5 000 000

Source

版权所有者: 郭家宝


从2017年2月一直做到2018年1月才A掉了这道题……
跨越了将近一整年……
当时咱为什么这么菜QAQ
现在也很菜就是了QAQ


思路:
首先,求出所有山脉的半平面交。

然后,枚举半平面交上半平面的交点和最外圈两条线与边界的交点(可能不存在),找出浮空岛位置。

接着,构造函数 f(x) 表示坐标 x 处山脉的高度,g(x)表示 x 处半平面的高度,需要找到minh(x)=g(x)f(x)
可以发现 h(x) 的极小值仅出现在半平面交点和山脉顶点上,枚举即可找出 minh(x) ,从而找出瞭望塔位置。

然后做完了…….

另外解析几何不知道比计算几何好写到哪里去了。

#include<bits/stdc++.h>
using namespace std;

inline int read()
{
    int x=0;char ch=getchar();
    while(ch<'0' || '9'<ch)ch=getchar();
    while('0'<=ch && ch<='9')x=x*10+(ch^48),ch=getchar();
    return x;
}

typedef double db;
typedef pair<db,db> point;
#define x first
#define y second
const int N=1000009;
const db eps=1e-8;

int n;
point p[N<<1];
int stk[N],top;

struct line
{
    db k,b;
    line(){};
    line(point aa,point bb)
    {
        k=(aa.y-bb.y)/(aa.x-bb.x);
        b=aa.y-k*aa.x;
    }
    bool operator < (line o)const
    {
        if(fabs(k-o.k)<eps)
            return b<o.b;
        return k<o.k;
    }
}l[N];

inline point cross(const line &a,const line &b)
{
    db x=(a.b-b.b)/(b.k-a.k);
    return point(x,a.k*x+a.b);
}

inline void hpi(int &m)
{
    for(int i=2;i<=n;i++)
        l[++m]=line(p[i-1],p[i]);
    sort(l+1,l+m+1);

    top=0;
    for(int i=1;i<=m;i++)
        if(i==m || fabs(l[i].k-l[i+1].k)>eps)
        {
            while(top>=2)
            {
                point pc=cross(l[stk[top]],l[i]);
                point pt=cross(l[stk[top]],l[stk[top-1]]);
                if(pc.x-pt.x<eps)top--;
                else break;
            }
            stk[++top]=i;
        }

    for(int i=1;i<=top;i++)
        l[i]=l[stk[i]];
    m=top;
}

inline bool chkmin(db &a,db b){if(a-b>eps){a=b;return 1;}return 0;}

inline db h(point a,line b,db c)
{
    return max(0.0,((b.k*a.x+b.b)-a.y)*c);
}

inline point getpt(db x,line a)
{
    return point(x,a.k*x+a.b);
}

inline db pos(db x,line a)
{
    return a.k*x+a.b;
}

inline bool in(point x)
{
    return p[1].x-x.x<eps && x.x-p[n].x<eps;
}

int main()
{
    n=read();
    for(int i=1;i<=n;i++)
        p[i].x=read(),p[i].y=read();
    sort(p+1,p+n+1);

    int m;
    hpi(m);

    point ans2=point(1e18f,1e18f),cr;

    for(int i=1,fl1=0,fl2=0;i<m;i++)
    {
        if(p[1].x<cross(l[i],l[i+1]).x && !fl1)
        {
            cr=getpt(p[1].x,l[i]);  
            if(ans2.y-cr.y>eps)
                ans2=cr;
            fl1=1;
        }
        if(p[n].x<cross(l[i],l[i+1]).x && !fl2)
        {
            cr=getpt(p[n].x,l[i]);  
            if(ans2.y-cr.y>eps)
                ans2=cr;
            fl2=1;
        }
    }
    if(m==1)
    {
        cr=getpt(p[1].x,l[1]);  
        if(ans2.y-cr.y>eps)
            ans2=cr;
        cr=getpt(p[n].x,l[1]);  
        if(ans2.y-cr.y>eps)
            ans2=cr;
    }

    for(int i=1;i<m;i++)
    {
        cr=cross(l[i],l[i+1]);
        if(in(cr) && ans2.y-cr.y>eps)
            ans2=cr;
    }

    point ans1=point(1e18f,1e18f);
    db mv=1e18f;
    int pi,pj;
    for(pi=1,pj=1;pi<=n && pj<m;)
    {
        point pti=p[pi];
        point ptj=cross(l[pj],l[pj+1]);
        if(pti.x<ptj.x)
        {
            if(chkmin(mv,h(pti,l[pj],1)))
                ans1=getpt(pti.x,l[pj]);
            pi++;
        }
        else
        {
            if(1<pi && chkmin(mv,h(ptj,line(p[pi-1],p[pi]),-1)))
                ans1=ptj;
            pj++;
        }
    }
    while(pi<=n)
    {
        point pti=p[pi++];
        if(chkmin(mv,h(pti,l[pj],1)))
            ans1=getpt(pti.x,l[pj]);
    }

    printf("%.2f %.2f\n",ans1.x,ans1.y);
    printf("%.2f %.2f\n",ans2.x,ans2.y);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值