传送门:bzoj4570
将妖怪的攻击力,防御力分别设为 x,y x , y 。
一开始把战斗力理解成 max(ba×x+y,ab×y+x) m a x ( b a × x + y , a b × y + x ) 了。实际上为 x+y+bax+aby x + y + b a x + a b y 。(懵逼了半天,雾)
为方便表示,首先设 k=−ba k = − b a ( a,b a , b 均为正实数, k<0 k < 0 )。则每个妖怪的战斗力为 x+y−kx−1ky x + y − k x − 1 k y ,对于单个妖怪,由均值不等式得到当 k=−yx−−√ k = − y x 时,战斗力最小。
可以将 x,y x , y 转化为平面上的点 (x,y) ( x , y ) 考虑, k k 即为过点 (x,y) ( x , y ) 的一条直线的斜率,而 x+y−kx−1ky x + y − k x − 1 k y 即为这条直线在 x,y x , y 轴上的截距的绝对值之和。所以只需要维护点的一个上凸壳(同样斜率的线,在凸壳上的点最先被扫到)。
再考虑求妖怪的最大战斗力,假设先将点按 x x 升序排序,若当前点 (x,y) ( x , y ) 战斗力大于上一个点 (x′,y′) ( x ′ , y ′ ) ,则有不等式:
显然也是上凸壳的形式。那么直接求出上凸壳,在凸壳上按 x x 从左到右遍历满足第 i i 个点有最强战斗力的斜率并结合满足其最小的斜率 −yx−−√ − y x 来更新答案即可。
#include
using namespace std;
typedef double db;
const int N=1e6+100;
const db eps=1e-8,inf=1e12;
int n,tot,cnt;db k,ka,kb,ans;
struct P{db x,y;}t[N],a[N];
inline P operator - (const P&A,const P&B){return (P){A.x-B.x,A.y-B.y};}
inline db cg(P A,P B){return A.x*B.y-A.y*B.x;}
inline int rd()
{
char ch=getchar();int x=0,f=1;
while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+(ch^48);ch=getchar();}
return x*f;
}
inline int dcmp(db x)
{
if(x-eps) return 0;
return x>0 ? 1:-1;
}
inline db minslope(P A){return -(sqrt((db)A.y/(db)A.x));}//满足值最小的斜率
inline bool cmp(const P&A,const P&B)
{return dcmp(A.x-B.x)==0? B.yinline db slope(P A,P B)
{
if(dcmp(A.x)==0 && dcmp(A.y)==0) return inf;
if(dcmp(B.x)==0 && dcmp(B.y)==0) return -inf;
if(dcmp(A.x-B.x)==0) return inf;
return (A.y-B.y)/(A.x-B.x);
}
inline db get(P p,db k){if(k>=0) return inf;return p.x+p.y-k*p.x-p.y/k;}
//更新答案
int main(){
int i,j;
n=rd();ans=inf;
for(i=1;i<=n;++i) scanf("%lf%lf",&t[i].x,&t[i].y);
if(n==1) {printf("%.4lf\n",get(t[1],minslope(t[1])));return 0;}
sort(t+1,t+n+1,cmp);
a[++tot]=t[1];
for(i=2;i<=n;++i){
while(tot>1 && dcmp(cg(a[tot]-a[tot-1],t[i]-a[tot]))>=0) tot--;
a[++tot]=t[i];
}//求凸包
ka=slope(a[1],a[2]);k=minslope(a[1]);
if(k>=ka) ans=min(ans,get(a[1],k));else ans=min(ans,get(a[1],ka));
kb=slope(a[tot-1],a[tot]);k=minslope(a[tot]);
if(k<=kb) ans=min(ans,get(a[tot],k));else ans=min(ans,get(a[tot],kb));
//初始化起点和终点
for(i=2;i1],a[i]);kb=slope(a[i],a[i+1]);
if(dcmp(k-ka)<=0 && dcmp(k-kb)>=0) ans=min(ans,get(a[i],k));//判断最小值是否在范围[ka,kb]内
else{ans=min(ans,get(a[i],ka));ans=min(ans,get(a[i],kb));}//否则双钩函数的边界一定是最优的
}
printf("%.4lf\n",ans);
}