bzoj2765[JLOI2010]铁人双项比赛
题意:铁人双项比赛由长跑和骑自行车组成,参赛选手必须先完成k公里的长跑,然后完成r公里的骑车,才能到达终点。参赛选手有的擅长长跑,有的擅长骑车。
如果总赛程s=k+r一定,那么K越大,对擅长长跑的选手越有利;k越小,对擅长骑车的选手越有利。
现在给定总赛程s,以及每个选手长跑和骑车的平均速度,请你求出对于某个指定的选手最有利的k和r。
所谓最有利,是指选择了这个k和r后,该选手可以获得冠军,且领先第2名尽量地多。
讲道理这题应该有SpecialJudge,但是BZOJ是在有多种方案时输出k最小的方案,题面上还没说…WA了一屏....
并不知道半平面交是什么东西(半瓶面胶),我写的是二分答案…首先我们把速度的单位从 千米/小时 转为 秒/千米,然后用i号选手的骑车速度减去n号选手的骑车速度,就表示i号选手和n号选手同时骑单位长度的车,n号选手会领先i号选手多少秒(负值表示n号选手落后i号选手).
接下来我们会发现,如果每长跑1千米n号选手会领先i号选手v1秒,每骑1千米车n号选手会领先i号选手v2秒(v1,v2均可能为负),二分答案时要求n号选手必须领先i号选手不少于ans秒,我们就可以确定一个关于k的不等式k*v1+(s-k)*v2>=ans,这里只有k未知,所以可以解出这个不等式.
每二分一个答案,一共得到n-1个形如”k>a”或”k<b”的不等式,只要判断这个不等式组有没有解即可.注意v1==v2的地方需要特判.最后一定要注意:输出解的时候k尽量小,所以最后利用二分出的答案解一遍不等式组,输出的时候用k的下界输出..
#include<cstdio> #include<algorithm> using namespace std; const int maxn=105; double v1[maxn],v2[maxn]; int s,n; bool check(double ans){ double upper=s,lower=0; for(int i=1;i<n;++i){ if(v1[i]==v2[i]){ if(s*v1[i]<ans)return false; } else if(v1[i]<v2[i])upper=min(upper,(ans-v2[i]*s)/(v1[i]-v2[i])); else lower=max(lower,(ans-v2[i]*s)/(v1[i]-v2[i])); } return upper>=lower; } void work(double ans){ double upper=s,lower=0; for(int i=1;i<n;++i){ if(v1[i]==v2[i])continue; else if(v1[i]<v2[i])upper=min(upper,(ans-v2[i]*s)/(v1[i]-v2[i])); else lower=max(lower,(ans-v2[i]*s)/(v1[i]-v2[i])); } printf("%.2f %.2f ",lower,s-lower); } int main(){ scanf("%d%d",&s,&n); for(int i=1;i<=n;++i){ scanf("%lf%lf",v1+i,v2+i); v1[i]=3600/v1[i];v2[i]=3600/v2[i]; } for(int i=1;i<n;++i){ v1[i]-=v1[n];v2[i]-=v2[n]; } double l=-1,r=1e50; while(r-l>=1e-8){ double mid=(l+r)/2.0; if(check(mid))l=mid; else r=mid; } if(r<0)printf("NO\n"); else{ work(r); printf("%.0f\n",r); } return 0; }