题目
题目大意
邱老师有n只妖怪,每只妖怪有攻击力atk和防御力dnf两种属性。
环境由a,b两个参数定义,在某种环境中,妖怪可以降低自己k×a点攻击力,提升k×b点防御力,或者,提升自己k×a点攻击力,降低k×b点防御力,a,b属于正实数,k为任意实数,但是atk和dnf必须始终非负。
妖怪在环境(a,b)中的战斗力为妖怪在该种环境中能达到的最大攻击力和最大防御力之和。
邱老师想知道在最为不利的情况下,所有妖怪中最强战斗力最低是多少。
(Bfk_zr缩减版 @Bfk_)
输入输出格式
输入格式:
第一行一个n,表示有n只妖怪。接下来n行,每行两个整数atk和dnf,表示妖怪的攻击力和防御力。
1≤n≤10^6, 0<atk,dnf≤10^8
输出格式:
输出在最不利情况下最强妖怪的战斗力值,保留4位小数。
解法
设攻击力为atk,防御力为def
根据「每上升a点攻击力就会下降b点防御力」,可以知道攻击力与防御力的函数是一条过[atk,def]的直线,斜率就是
−ba
−
b
a
(横坐标atk,纵坐标def)
那么把所有的[atk,def]画在平面上,可以发现战斗力最大的妖怪一定在上凸包上,因此用切线绕着凸包滚一圈就可以了。
关键是怎么滚,因为斜率是double类型,总不可能k+=eps
于是考虑每只妖怪的战斗力,那么有:
Max_atk=atk+def∗ab
M
a
x
_
a
t
k
=
a
t
k
+
d
e
f
∗
a
b
Max_def=def+atk∗ba
M
a
x
_
d
e
f
=
d
e
f
+
a
t
k
∗
b
a
CE=Max_atk+Max_def=atk+def+def∗k+atk∗1k
C
E
=
M
a
x
_
a
t
k
+
M
a
x
_
d
e
f
=
a
t
k
+
d
e
f
+
d
e
f
∗
k
+
a
t
k
∗
1
k
根据均值不等式,当
k
k
取 时,
CE
C
E
有最小值为
atk+def+2∗atk∗def−−−−−−−√
a
t
k
+
d
e
f
+
2
∗
a
t
k
∗
d
e
f
可以发现,如果
k
k
可以取 ,那么取这个斜率一定是最优的。
然而如果这个斜率的直线插进了凸包内部,就不能取这个斜率,而只能取凸包上与该点相邻的另外两个点与之连线的斜率来更新答案。
看起来比较有难度,写起来还是很轻松的
下面是自带大常数的代码
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;
const double eps = 1e-8 ;
int N , topp ;
struct Vector{
double x , y ;
double len(){ return sqrt( x * x + y * y ) ; }
Vector(){} ;
Vector( double x_ , double y_ ):
x(x_) , y(y_) {} ;
};
typedef Vector Point ;
typedef Vector Vv ;
Point a[1000005] , p[1000005] ;
int dcmp( const double &x ){
if( x > -eps && x < eps ) return 0 ;
return x > eps ? 1 : -1 ;
}
Vv operator + ( const Vv &A , const Vv &B ){ return Vv( A.x+B.x , A.y+B.y ) ;}
Vv operator - ( const Vv &A , const Vv &B ){ return Vv( A.x-B.x , A.y-B.y ) ;}
Vv operator * ( const Vv &A , const double &p ){ return Vv( A.x*p , A.y*p ) ;}
Vv operator / ( const Vv &A , const double &p ){ return Vv( A.x/p , A.y/p ) ;}
double Dot ( const Vv &A , const Vv &B ){ return A.x * B.x + A.y * B.y ; }
double Cross( const Vv &A , const Vv &B ){ return A.x * B.y - A.y * B.x ; }
bool operator ==( const Vv &A , const Vv &B ){ return A.x == B.x && A.y == B.y;}
bool operator < ( const Vv &A , const Vv &B ){
return A.x < B.x ||
( A.x == B.x && A.y < B.y ) ;
}
void convex( Point *a , const int &siz , Point *p , int &topp ){
sort( a + 1 , a + siz + 1 ) ;
topp = 0 , p[topp] = Point( -1234567890.0 , 0 ) ;
for( register int i = 1 ; i <= siz ; i ++ ){
while( topp >= 1 && Cross( p[topp] - p[topp-1] , a[i] - p[topp-1] ) > 0 ) topp -- ;
p[++topp] = a[i] ;
}
}
double K( const Point &A , const Point &B ){
return ( A.y - B.y ) / ( A.x - B.x ) ;
}
void solve(){
double ans = 12345678900.0 ;
for( register int i = 1 ; i <= topp ; i ++ ){
double nowK = -( sqrt( p[i].y ) / sqrt( p[i].x ) ) ,
preK = ( i - 1 ? K( p[i-1] , p[i] ) : 0 ) ,
nxtK = ( i + 1 <= topp ? K( p[i] , p[i+1] ) : -1234567890.0 ) ;
if( nowK > preK )
ans = min( ans , p[i].x * ( 1 - preK ) + p[i].y * ( 1 - 1/preK ) ) ;
else if( nowK < nxtK )
ans = min( ans , p[i].x * ( 1 - nxtK ) + p[i].y * ( 1 - 1/nxtK ) ) ;
else
ans = min( ans , p[i].x * ( 1 - nowK ) + p[i].y * ( 1 - 1/nowK ) ) ;
}
printf( "%.4f" , ans ) ;
}
int main(){
scanf( "%d" , &N ) ;
for( register int i = 1 ; i <= N ; i ++ )
scanf( "%lf%lf" , &a[i].x , &a[i].y ) ;
convex( a , N , p , topp ) ;
solve() ;
}