[SCOI2016]妖怪(另类nlogn做法)
题意
做法
前两种做法参考博客:https://www.luogu.com.cn/blog/ChenXingLing/solution-p3291
这里就不再赘述,不过需要提一下,第二种做法感觉有点问题的地方是:\(k\)应该在\([k2,k1]\)范围内。(当然,仅仅个人观点,不一定正确)
然后我自己手艹出一个做法?
首先,已知对于\((a,b)\),可以化作\((1,\frac{b}{a})\)的形式,不妨化为:\((1,k)\),这样,就顺利的化成了一个元的形式了。
那么对于一只妖怪,其战斗力函数为:\(a+b+ak+\frac{b}{k}\)。
不难发现,如果对于两个妖怪:\(a_{1}≤a_2,b_1≤b_2\)那么第一个妖怪绝对没有第二个妖怪好,此时,自动把第一个妖怪舍弃,然后再对于\(a\)进行升序排序,这样,妖怪就是\(a\)值递增,\(b\)值递减了。
考虑第一个妖怪在哪个大于第二个妖怪:
设\(a'=a_{1}-a_{2},b'=b_{1}-b_{2}\)
那么不等式方程则为:
\(a'k+\frac{b'}{k}+a'+b'≥0\)
\(a'k^2+(a'+b')k+b'≥0\)
\(k∈[-1,0)∩(0,-\frac{b'}{a'}]\),由于\(k>0\),所以\(k∈(0,-\frac{b'}{a'}]\)。
也就是说,第一个妖怪在这个区间都比第二个区间好。
那么只要我们能够找到这些妖怪各自的最强的区间,然后对他们的函数在他们区间中的最小值取最小值即可。
但是如何证明一个函数的\(k\)存在一个取值区间这个函数值大于其他的函数值呢?
再次证明一个东西:\(\frac{b_1-b_2}{a_1-a_2}>\frac{b_1-b_3}{a_1-a_3}\)可以推导出:\(\frac{b_2-b_3}{a_2-a_3}<\frac{b_{1}-b_2}{a_1-a_2}\)。
证明过程就是交叉相乘,后面发现两条式子可以化成一条式子,那么这条式子说明了什么了?下面设\(A_i\)为第一个函数,\(jd()\)为交点,说明\(jd(A_1,A_3)<jd(A_1,A_2)\),那么\(jd(A_2,A_3)<jd(A_1,A_2)\),简单来说就是\(A_2\)的\(k\)不可能存在一个取值区间使得这个函数值大于其他函数值。
同理,只要用栈维护每个函数,弹出时判断与\(A_1\)的交点即可,然后\(A_i\)的\(k\)的取值区间则在:[\(jd(A_{i-1},A_i)\),\(jd(A_{i},A_{i+1})\)]范围内。
至于函数的最小值,额,直接均值不等式,这个不再多讲了。
时间复杂度:\(O(nlogn)\)
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define N 1100000
using namespace std;
template<class T>
inline T mymin(T x,T y){return x<y?x:y;}
template<class T>
inline T mymax(T x,T y){return x>y?x:y;}
int n;
struct node
{
double a,b;
node(double x=0,double y=0){a=x;b=y;}
}a[N],b[N];int m;
inline node operator-(node x,node y){return node(x.a-y.a,x.b-y.b);}
inline double jd(node x)//求交点
{
return -x.b/x.a;//(x.a)k^2+(x.a+x.b)k+x.b k=-x.b/x.a(其中x.a<0,x.b>0)
}
inline double getval(node x,double k){return x.a+x.b+x.a*k+x.b/k;}
inline double mmin(node x,double l,double r)
{
double minx=sqrt(x.b/x.a),ans;//最小的数字
if(l<=minx && minx<=r || (l<=minx && r<0))ans=minx;
else if(minx<l)ans=l;
else ans=r;
return getval(x,ans);
}
inline bool cmp(node x,node y){return x.a==y.a?x.b>y.b:x.a<y.a;}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%lf%lf",&a[i].a,&a[i].b);
sort(a+1,a+n+1,cmp);
int now=a[1].a;
b[m=1]=a[1];
for(int i=2;i<=n;i++)
{
if(a[i].a!=now)
{
now=a[i].a;
while(m && b[m].b<=a[i].b)m--;//维护a单调递增,b单调递减
while(m>1 && jd(b[1]-b[m])>=jd(b[1]-a[i]))m--;
b[++m]=a[i];
}
}
if(m==1)printf("%.4lf\n",getval(b[1],sqrt(b[1].b/b[1].a)));
else
{
double ans=mymin(mmin(b[1],-1,jd(b[1]-b[2])),mmin(b[m],jd(b[m-1]-b[m]),-1));
for(int i=2;i<m;i++)ans=mymin(mmin(b[i],jd(b[i-1]-b[i]),jd(b[i]-b[i+1])),ans);
printf("%.4lf\n",ans);
}
return 0;
}