bzoj-2765 铁人双项比赛

题意:

现在有n个选手和长度为s的赛道;

现要将赛道分为前后两段,前半段长度为k;

第i个选手在前半段的速度是v1,后半段的速度是v2;

求一种对第n个选手最有利的一个k,使其领先第二名尽可能多;

如果他不可能第一,输出NO;


题解:
列出第i个选手到终点的时间ti=k/v1i+(n-k)/v2i;

化简ti=(1/v1i-1/v2i)*k+n/v2i;

设k为x轴,时间为y轴,那么这时一条直线;

我们对前n-1名选手的直线求半平面交,得到对每个k,n-1个人中谁最快;

而对于第n名选手,最优的k一定在这个凸包与直线斜率相切的时候;

我们二分出这个位置,然后求出所有选手在这个k下的时间与选手n的作比较;

然后判断输出即可,注意最优的k可能为负,这时要将其调整为0;


代码:


#include<math.h>
#include<stdio.h>
#include<iomanip>
#include<string.h>
#include<iostream>
#include<algorithm>
#define N 110
using namespace std;
typedef long double ld;
ld v1[N],v2[N];
struct Point
{
    ld x,y;
    Point(){}
    Point(ld _,ld __):x(_),y(__){}
    friend Point operator +(Point a,Point b)
    {
        return Point(a.x+b.x,a.y+b.y);
    }
    friend Point operator -(Point a,Point b)
    {
        return Point(a.x-b.x,a.y-b.y);
    }
    friend Point operator *(ld a,Point b)
    {
        return Point(a*b.x,a*b.y);
    }
    friend ld operator ^(Point a,Point b)
    {
        return a.x*b.y-a.y*b.x;
    }
}p[N];
struct Line
{
    Point p,v;
    ld alpha;
    Line(){}
    Line(Point _,Point __){p=_,v=__,alpha=atan2(v.y,v.x);}
    friend bool operator <(Line a,Line b)
    {
        return a.alpha<b.alpha;
    }
    ld operator [](ld x)
    {
        return (p+x*v).y;
    }
    friend bool Onleft(Line a,Point b)
    {
        return (a.v^b-a.p)>0;
    }
    friend Point getP (Line a,Line b)
    {
        Point u=a.p-b.p;
        ld temp=(b.v^u)/(a.v^b.v);
        return a.p+temp*a.v;
    }
}l[N],q[N];
int st,en;
void HPI(int n)
{
    int i;
    q[st=en=1]=l[1];
    for(i=2;i<=n;i++)
    {
        while(st<en&&Onleft(l[i],p[en]))
            en--;
        if(fabs(l[i].alpha-q[en].alpha)<1e-6)
            q[en]=Onleft(l[i],q[en].p)?q[en]:l[i];
        else
            q[++en]=l[i];
        p[en]=getP(q[en-1],q[en]);
    }
}
int main()
{
    int n,i,j,k;
    ld s,ans;
    cin>>s>>n;
    for(i=1;i<=n;i++)
    {
        cin>>v1[i]>>v2[i];
        l[i]=Line(Point(0,s/v2[i]),Point(1,1/v1[i]-1/v2[i]));
    }
    sort(l+1,l+n);
    HPI(n-1);
    k=lower_bound(q+st,q+en+1,l[n])-q;
    p[k].x=min(s,max((ld)0,p[k].x));
    for(i=1,ans=1e100;i<n;i++)
    {
        ans=min(ans,l[i][p[k].x]-l[n][p[k].x]);
    }
    if(ans>=0)
        cout<<fixed<<setprecision(2)<<p[k].x<<' '<<s-p[k].x<<' ',
        cout<<fixed<<setprecision(0)<<ans*60*60<<endl;
        else
        puts("NO");
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值