[BZOJ4570][SCOI2016]妖怪(凸包)
两种做法,前一种会TLE。
第一种是高一数学题做法,设一个妖怪的atk和dnf分别为x和y,则它在(a,b)环境下的战斗力为x+y/a*b+y+x/a*b。
设t为b/a,则战斗力即$f(x,y,t)=x+y+tx+\frac{y}{t}$,其中$t\in(0,+\infty)$。
二分答案c,问题转化为求是否存在t满足,对于所有妖怪,都有$f(x,y,t)<=c$。
两边同乘t并移项,得$xt^2+(x+y-c)t+y<=0$,可以通过解二次不等式得出t的范围。所有妖怪的可行t的交集不为空则c可行。
复杂度$O(n\log INF)$
第二种将每个妖怪看成点(x,y),每个环境看成斜率为$-\frac{b}{a}$的直线,则每个妖怪在某环境下的战斗力就是过它的直线x,y轴截距之和,即$x+y-kx-\frac{y}{k}$。
显然可能更新答案的点一定在上凸包上。建立上凸包,某斜率下的妖怪战斗力最大值就是该直线切凸包时的横纵截距和。
根据对勾函数性质可知,$-kx-\frac{y}{k}$的最大值在$k_0=-\sqrt{\frac{y}{x}}$时取到。
对于凸包上的每个点,若这个点的$k_0$与凸包的切点就是这个点,则用它更新答案,否则,由于对勾函数在极值点两侧都是单调的,所以对每个点都只需要考虑它与左边的点和右边的点连成的直线即可。
复杂度瓶颈在排序,$O(n\log n)$
1 #include<cmath> 2 #include<cstdio> 3 #include<algorithm> 4 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 5 using namespace std; 6 7 const int N=1000010; 8 double ans=1e30,eps=1e-10; 9 int n,top; 10 struct P{ double x,y; }p[N],s[N]; 11 bool operator <(const P &a,const P &b){ return (a.x==b.x) ? a.y>b.y : a.x<b.x; } 12 13 double chk(P &a,P &b,P &c){ return (b.x-a.x)*(c.y-b.y)-(c.x-b.x)*(b.y-a.y); } 14 double sl(P &a,P &b){ return (b.y-a.y)/(b.x-a.x); } 15 double cal(P &a,double k){ return (fabs(k)<eps) ? 1e30 : a.x+a.y-a.x*k-a.y/k; } 16 17 int main(){ 18 freopen("bzoj4570.in","r",stdin); 19 freopen("bzoj4570.out","w",stdout); 20 scanf("%d",&n); 21 rep(i,1,n) scanf("%lf%lf",&p[i].x,&p[i].y); 22 sort(p+1,p+n+1); 23 rep(i,1,n){ 24 if (fabs(s[top].x-p[i].x)<eps) continue; 25 while (top>1 && chk(s[top-1],s[top],p[i])>0) top--; 26 s[++top]=p[i]; 27 } 28 rep(i,1,top){ 29 double k=-sqrt(s[i].y/s[i].x); 30 if ((i==1 || k<=sl(s[i-1],s[i])) && (i==top || k>=sl(s[i],s[i+1]))) ans=min(ans,cal(s[i],k)); 31 if (i>1) ans=min(ans,cal(s[i],sl(s[i-1],s[i]))); 32 } 33 printf("%.4lf\n",ans); 34 return 0; 35 }