[SCOI2016]妖怪(另类nlogn做法)

题意

题意

做法

前两种做法参考博客:https://www.luogu.com.cn/blog/ChenXingLing/solution-p3291

这里就不再赘述,不过需要提一下,第二种做法感觉有点问题的地方是: k k k应该在 [ k 2 , k 1 ] [k2,k1] [k2,k1]范围内。(当然,仅仅个人观点,不一定正确)

然后我自己手艹出一个做法?

首先,已知对于 ( a , b ) (a,b) (a,b),可以化作 ( 1 , b a ) (1,\frac{b}{a}) (1,ab)的形式,不妨化为: ( 1 , k ) (1,k) (1,k),这样,就顺利的化成了一个元的形式了。

那么对于一只妖怪,其战斗力函数为: a + b + a k + b k a+b+ak+\frac{b}{k} a+b+ak+kb

不难发现,如果对于两个妖怪: a 1 ≤ a 2 , b 1 ≤ b 2 a_{1}≤a_2,b_1≤b_2 a1a2,b1b2那么第一个妖怪绝对没有第二个妖怪好,此时,自动把第一个妖怪舍弃,然后再对于 a a a进行升序排序,这样,妖怪就是 a a a值递增, b b b值递减了。

考虑第一个妖怪在哪个大于第二个妖怪:
a ′ = a 1 − a 2 , b ′ = b 1 − b 2 a'=a_{1}-a_{2},b'=b_{1}-b_{2} a=a1a2,b=b1b2
那么不等式方程则为:
a ′ k + b ′ k + a ′ + b ′ ≥ 0 a'k+\frac{b'}{k}+a'+b'≥0 ak+kb+a+b0
a ′ k 2 + ( a ′ + b ′ ) k + b ′ ≥ 0 a'k^2+(a'+b')k+b'≥0 ak2+(a+b)k+b0
k ∈ [ − 1 , 0 ) ∩ ( 0 , − b ′ a ′ ] k∈[-1,0)∩(0,-\frac{b'}{a'}] k[1,0)(0,ab],由于 k > 0 k>0 k>0,所以 k ∈ ( 0 , − b ′ a ′ ] k∈(0,-\frac{b'}{a'}] k(0,ab]

也就是说,第一个妖怪在这个区间都比第二个区间好。

那么只要我们能够找到这些妖怪各自的最强的区间,然后对他们的函数在他们区间中的最小值取最小值即可。

但是如何证明一个函数的 k k k存在一个取值区间这个函数值大于其他的函数值呢?

再次证明一个东西: b 1 − b 2 a 1 − a 2 > b 1 − b 3 a 1 − a 3 \frac{b_1-b_2}{a_1-a_2}>\frac{b_1-b_3}{a_1-a_3} a1a2b1b2>a1a3b1b3可以推导出: b 2 − b 3 a 2 − a 3 < b 1 − b 2 a 1 − a 2 \frac{b_2-b_3}{a_2-a_3}<\frac{b_{1}-b_2}{a_1-a_2} a2a3b2b3<a1a2b1b2
证明过程就是交叉相乘,后面发现两条式子可以化成一条式子,那么这条式子说明了什么了?下面设 A i A_i Ai为第一个函数, j d ( ) jd() jd()为交点,说明 j d ( A 1 , A 3 ) < j d ( A 1 , A 2 ) jd(A_1,A_3)<jd(A_1,A_2) jd(A1,A3)<jd(A1,A2),那么 j d ( A 2 , A 3 ) < j d ( A 1 , A 2 ) jd(A_2,A_3)<jd(A_1,A_2) jd(A2,A3)<jd(A1,A2),简单来说就是 A 2 A_2 A2 k k k不可能存在一个取值区间使得这个函数值大于其他函数值。

同理,只要用栈维护每个函数,弹出时判断与 A 1 A_1 A1的交点即可,然后 A i A_i Ai k k k的取值区间则在:[ j d ( A i − 1 , A i ) jd(A_{i-1},A_i) jd(Ai1,Ai), j d ( A i , A i + 1 ) jd(A_{i},A_{i+1}) jd(Ai,Ai+1)]范围内。

至于函数的最小值,额,直接均值不等式,这个不再多讲了。

时间复杂度: O ( n l o g n ) O(nlogn) 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值