传送门: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−kx−1ky x + y − k x − 1 k y 即为这条直线在 x,y x , y 轴上的截距的绝对值之和。所以只需要维护点的一个上凸壳(同样斜率的线,在凸壳上的点最先被扫到)。
再考虑求妖怪的最大战斗力,假设先将点按
x
x
升序排序,若当前点战斗力大于上一个点
(x′,y′)
(
x
′
,
y
′
)
,则有不等式:
化简得到:
显然也是上凸壳的形式。那么直接求出上凸壳,在凸壳上按 x x 从左到右遍历满足第个点有最强战斗力的斜率并结合满足其最小的斜率 −yx−−√ − y x 来更新答案即可。
代码
#include<bits/stdc++.h>
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 && 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.y<A.y:A.x<B.x;}
inline 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;i<tot;++i){
k=minslope(a[i]);ka=slope(a[i-1],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);
}

博客内容介绍了如何解决BZOJ 4570题目中的妖怪战斗力计算问题。通过将攻击力和防御力转换为平面上的点,并利用上凸壳算法找到妖怪战斗力的最小值。文章详细解释了如何利用均值不等式确定最佳战斗力,并提供了求解最大战斗力的思路,涉及到排序和上凸壳遍历的过程。
2003

被折叠的 条评论
为什么被折叠?



