[BZOJ2756][JLOI2010]铁人双项比赛(半平面交+三分法)

211 篇文章 0 订阅
55 篇文章 0 订阅

题目描述

传送门

题目大意:n个人参加比赛,先跑步和自行车的总路程为s,其中跑步为k,走路为r,每个人跑步和自行车都有一个速度。求出对第n个人最有利的k和r,使其获得冠军,并且领先第二名的时间最多。

题解

首先将每个人的k-时间方程写出来 y=x/v1+(s-x)/v2=(1/v1-1/v2)x+s/v2
这样得到了n个方程,用半平面交求凸壳(其它比半平面交高明到不知道哪里去了的办法都资瓷
求出凸壳之后判断第n条直线是否在凸壳上有位置(有一个点也包括),如果有的话,就得到了一个合法的区间,直观观察可以发现和第二名的差距在区间上是单峰的,可以三分求解

代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
#define N 105

const double eps=1e-9;
const double inf=1e9;
int dcmp(double x)
{
    if (x<=eps&&x>=-eps) return 0;
    return (x>0)?1:-1;
}
struct Vector
{
    double x,y;
    Vector(double X=0,double Y=0)
    {
        x=X,y=Y;
    }
};
typedef Vector Point;
struct Line
{
    double k,b;
    Point p,q;
    Line(double K=0,double B=0)
    {
        k=K,b=B;
    }
};
Vector operator + (Vector a,Vector b){return Vector(a.x+b.x,a.y+b.y);}
Vector operator - (Vector a,Vector b){return Vector(a.x-b.x,a.y-b.y);}
Vector operator * (Vector a,double b){return Vector(a.x*b,a.y*b);}
int n,cnt;
double s;
Line line[N];
Point poly[N],npoly[N];

double Cross(Vector a,Vector b){return a.x*b.y-a.y*b.x;}
bool ins(Point A,Point B,Point C,Point D)
{
    Vector v,u,w;
    v=B-A,u=C-A,w=D-A;
    if (dcmp(Cross(v,u))==dcmp(Cross(v,w))) return 0;
    v=D-C,u=A-C,w=B-C;
    if (dcmp(Cross(v,u))==dcmp(Cross(v,w))) return 0;
    return 1;
}
Point GLI(Point P,Vector v,Point Q,Vector w)
{
    Vector u=P-Q;
    double t=Cross(w,u)/Cross(v,w);
    return P+v*t;
}
void init()
{
    cnt=0;
    poly[++cnt]=Point(0,0);
    poly[++cnt]=Point(0,inf);
    poly[++cnt]=Point(s,inf);
    poly[++cnt]=Point(s,0);
}
void halfp(Point A,Point B)
{
    Point C,D;int ncnt=0;
    for (int i=1;i<=cnt;++i)
    {
        C=poly[i];
        D=poly[i%cnt+1];
        if (dcmp(Cross(C-A,B-A))>=0)
            npoly[++ncnt]=C;
        if (ins(A,B,C,D))
            npoly[++ncnt]=GLI(A,B-A,C,D-C);
    }
    cnt=ncnt;
    for (int i=1;i<=cnt;++i) poly[i]=npoly[i];
}
double check(double x)
{
    double Min=inf;
    for (int i=1;i<n;++i)
        Min=min(Min,line[i].k*x+line[i].b);
    return Min-(line[n].k*x+line[n].b);
}
double find(double l,double r)
{
    double mid1,mid2,ans1,ans2,ans;
    while (r-l>eps)
    {
        mid1=l+(r-l)/3.0;
        mid2=r-(r-l)/3.0;
        ans1=check(mid1);
        ans2=check(mid2);
        if (ans1>=ans2) ans=mid1,r=mid2;
        else ans=mid2,l=mid1;
    }
    return ans;
}
int main()
{
    scanf("%lf%d",&s,&n);
    for (int i=1;i<=n;++i)
    {
        double v1,v2;scanf("%lf%lf",&v1,&v2);
        line[i]=Line(1/v1-1/v2,s/v2);
        line[i].p.x=0,line[i].p.y=line[i].b;
        line[i].q.x=s,line[i].q.y=line[i].k*s+line[i].b;
    }
    init();
    for (int i=1;i<=n;++i)
        halfp(line[i].p,line[i].q);
    bool flag=0;double  ans;
    for (int i=1;i<=cnt;++i)
    {
        double xa=poly[i].x,ya=poly[i].y,xb=poly[i%cnt+1].x,yb=poly[i%cnt+1].y;
        if (!dcmp(xa-xb)||(!dcmp(ya)&&!dcmp(yb))) continue;
        double know=(ya-yb)/(xa-xb);
        double bnow=ya-xa*know;
        if (!dcmp(line[n].k-know)&&!dcmp(line[n].b-bnow))
        {
            flag=1;
            ans=find(poly[i].x,poly[i%cnt+1].x);
            break;
        }
    }
    if (!flag)
    {
        for (int i=1;i<=cnt;++i)
            if (!dcmp(line[n].k*poly[i].x+line[n].b-poly[i].y))
            {
                flag=1;
                ans=poly[i].x;
                break;
            }
        if (!flag) puts("NO");
        else printf("%.2lf %.2lf %.0lf\n",ans,s-ans,check(ans)*3600+eps);
    }
    else printf("%.2lf %.2lf %.0lf\n",ans,s-ans,check(ans)*3600+eps);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值