SCOI2016 妖怪
妖怪
邱老师是妖怪爱好者,他有 \(n\) 只妖怪,每只妖怪有攻击力 \(\text{atk}\) 和防御力 \(\text{dnf}\) 两种属性。邱老师立志成为妖怪大师,于是他从真新镇出发,踏上未知的旅途,见识不同的风景。环境对妖怪的战斗力有很大影响,在某种环境中,妖怪可以降低自己 \(ka\) 点攻击力,提升 \(kb\) 点防御力或者,提升自己 \(ka\) 点攻击力,降低 \(kb\) 点防御力。\(a, b\) 属于正实数,\(k\) 为任意实数,但是 \(\text{atk}\) 和 \(\text{dnf}\) 必须始终非负。
妖怪在环境 \((a, b)\) 中的战斗力为妖怪在该种环境中能达到的最大攻击力和最大防御力之和,\(\text{strength}(a, b) = \max(\text{atk}(a, b)) + \max(\text{dnf}(a, b))\)。环境由 \(a, b\) 两个参数定义,\(a, b\) 的含义见前文描述。比如当前环境 \((a = 3, b = 2)\),那么攻击力为 \(6\),防御力为 \(2\) 的妖怪,能达到的最大攻击力为 \(9\),最大防御力为 \(6\)。所以该妖怪在 \((a = 3, b = 2)\) 的环境下战斗力为 \(15\)。因此,在不同的环境,战斗力最强的妖怪可能发生变化。
作为一名优秀的妖怪训练师,邱老师想发掘每一只妖怪的最大潜力,他想知道在最为不利的情况下,他的 \(n\) 只妖怪能够达到的最强战斗力值,即存在一组正实数 \((a, b)\) 使得 \(n\) 只妖怪在该环境下最强战斗力最低。
\(1 \leq n \leq 10 ^ 6, 0 < \text{atk}, \text{dnf} \leq 10 ^ 8\)
分析
https://www.cnblogs.com/Skyminer/p/6431746.html
我们知道如果一个怪物要取到攻击力的最大值,那么一定是把防御力都转化了。所以我们可以把题目转化成这个式子
我们设\(k = -\frac{b}{a}\),那么上式变为
右侧括号里是对勾函数的形式,我们由数学知识得\(k = -\sqrt{\frac{dnf}{atk}}\)时取得最值
现在我们尝试把所有怪物的属性看成点映射到二维平面。
设点\((x,y)\),假设通过了一条斜率为\(k(k < 0)\)的直线,我们通过计算发现这个直线的横纵截距之和即为我们上面的计算式。
而当\(k = -\sqrt{\frac{dnf}{atk}}\)横纵截距之和最小,所以问题转化成了为二维平面上的一些点确定一些平行线通过这些点使得最大的横纵截距之和最小。
我们知道最大的条直线一定是通过上凸壳上的点的,所以我们枚举上凸壳上的点即可。
时间复杂度\(O(n \log n)\)
用
operator*
代替Dot
,用operator/
代替Cross
。不推荐。
co double eps=1e-8;
typedef struct Point {double x,y;}Vector;
Vector operator+(co Vector&a,co Vector&b) {return (Vector){a.x+b.x,a.y+b.y};}
Vector operator-(co Vector&a,co Vector&b) {return (Vector){a.x-b.x,a.y-b.y};}
double operator*(co Vector&a,co Vector&b) {return a.x*b.x+a.y*b.y;}
double operator/(co Vector&a,co Vector&b) {return a.x*b.y-a.y*b.x;}
double min_p(co Point&x) {return -sqrt(x.y/x.x);}
int dcmp(double x) {return fabs(x)<eps?0:(x<0?-1:1);}
double slope(co Point&a,co Point&b){
if(dcmp(a.x)==0&&dcmp(a.y)==0) return 1e10;
if(dcmp(b.x)==0&&dcmp(b.y)==0) return -1e10;
if(dcmp(a.x-b.x)==0) return 1e10;
return (b.y-a.y)/(b.x-a.x);
}
bool cmp(co Point&a,co Point&b) {return dcmp(a.x-b.x)==0?a.y<b.y:a.x<b.x;}
double calc(co Point&p,double k){
if(k>=0) return 1e10;
return p.x+p.y-k*p.x-p.y/k;
}
co int N=1e6+1;
Point p[N],ch[N];
int n,m;
void convex(){
ch[m=1]=p[1];
for(int i=2;i<=n;++i){
while(m>1&&dcmp((ch[m]-ch[m-1])/(p[i]-ch[m]))>=0) --m;
ch[++m]=p[i];
}
std::swap(n,m),std::swap(p,ch);
}
int main(){
read(n);
for(int i=1;i<=n;++i) read(p[i].x),read(p[i].y);
if(n==1) return printf("%.4lf\n",calc(p[1],min_p(p[1]))),0;
std::sort(p+1,p+n+1,cmp),convex();
double ans=1e10;
double k1,k2,k;
k2=slope(p[1],p[2]), k=min_p(p[1]);if(k>=k2) ans=std::min(ans,calc(p[1],k));
k1=slope(p[n-1],p[n]),k=min_p(p[n]);if(k<=k1) ans=std::min(ans,calc(p[n],k));
ans=std::min(ans,calc(p[n],k1));
for(int i=2;i<n;++i){
k=min_p(p[i]),k1=slope(p[i-1],p[i]),k2=slope(p[i],p[i+1]);
ans=std::min(ans,calc(p[i],k1));
if(dcmp(k-k1)<=0&&dcmp(k-k2)>=0) ans=std::min(ans,calc(p[i],k));
}
printf("%.4lf\n",ans);
return 0;
}