bzoj4570【SCOI2016】妖怪

148 篇文章 0 订阅
8 篇文章 0 订阅

4570: [Scoi2016]妖怪

Time Limit: 10 Sec   Memory Limit: 64 MB
Submit: 410   Solved: 108
[ Submit][ Status][ Discuss]

Description

邱老师是妖怪爱好者,他有n只妖怪,每只妖怪有攻击力atk和防御力dnf两种属性。邱老师立志成为妖怪大师,于
是他从真新镇出发,踏上未知的旅途,见识不同的风景。环境对妖怪的战斗力有很大影响,在某种环境中,妖怪可
以降低自己k×a点攻击力,提升k×b点防御力或者,提升自己k×a点攻击力,降低k×b点防御力,a,b属于正实数
,k为任意实数,但是atk和dnf必须始终非负。妖怪在环境(a,b)中的战斗力为妖怪在该种环境中能达到的最大攻击
力和最大防御力之和。strength(a,b)=max(atk(a,b))+max(dnf(a,b))环境由a,b两个参数定义,a,b的含义见前
文描述。比如当前环境a=3,b=2,那么攻击力为6,防御力为2的妖怪,能达到的最大攻击力为9,最大防御力为6。
所以该妖怪在a=3,b=2的环境下战斗力为15。因此,在不同的环境,战斗力最强的妖怪可能发生变化。作为一名优
秀的妖怪训练师,邱老师想发掘每一只妖怪的最大潜力,他想知道在最为不利的情况下,他的n只妖怪能够达到的
最强战斗力值,即存在一组正实数(a,b)使得n只妖怪在该环境下最强战斗力最低。

Input

第一行一个n,表示有n只妖怪。接下来n行,每行两个整数atk和dnf,表示妖怪的攻击力和防御力。
1≤n≤10^6, 0<atk,dnf≤10^8

Output

 输出在最不利情况下最强妖怪的战斗力值,保留4位小数。

Sample Input

3
1 1
1 2
2 2

Sample Output

8.0000



凸包

如果把妖怪的攻击值和防御值分别看为横坐标和纵坐标,则妖怪的每一个状态对应二维平面上一个点,而根据定义,妖怪的状态实际是一条斜率是-b/a的直线,而战斗力则为该直线的横纵截距之和。

那么每一个有序数对(a,b),都对应一组斜率固定的直线。

战斗力最大的妖怪一定是相对这些直线最靠上的。

换一种方式考虑,求出这些点的右上凸包,然后每个点对应的斜率都有一个范围。这个点的战斗力可以变化成一个对勾函数,分类讨论一下最值问题,再对于凸包上所有点的答案取最小值。




#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define F(i,j,n) for(int i=j;i<=n;i++)
#define D(i,j,n) for(int i=j;i>=n;i--)
#define ll long long
#define N 1000005
#define inf 1e60
#define eps 1e-10
using namespace std;
int n,top;
struct P{ll x,y;}p[N],s[N];
double ans=inf,k1,k2,k;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
inline P operator -(const P &a,const P &b){return (P){a.x-b.x,a.y-b.y};}
inline ll operator *(const P &a,const P &b){return a.x*b.y-a.y*b.x;}
inline bool operator <(const P &a,const P &b){return a.x<b.x||(a.x==b.x&&a.y<b.y);}
void solve()
{
	sort(p+1,p+n+1);
	s[++top]=p[1];
	F(i,2,n)
	{
		while (top>=2&&(s[top]-s[top-1])*(p[i]-s[top-1])>=0) top--;
		s[++top]=p[i];
	}
}
double get(P a){return -sqrt((double)a.y/(double)a.x);}
inline double getk(P a,P b){return a.x==b.x?inf:(double)(a.y-b.y)/(double)(a.x-b.x);}
double calc(P a,double k){return k>=0?inf:(double)a.x+a.y-a.x*k-a.y/k;}
int main()
{
	n=read();
	F(i,1,n) p[i].x=read(),p[i].y=read();
	solve();
	if (top==1){printf("%.4lf\n",calc(s[1],get(s[1])));return 0;}
	k2=getk(s[1],s[2]);k=get(s[1]);
	if (k>=k2) ans=min(ans,calc(s[1],k));
	k1=getk(s[top-1],s[top]);k=get(s[top]);
	if (k<=k1) ans=min(ans,calc(s[top],k));
	ans=min(ans,calc(s[top],k1));
	F(i,2,top-1)
	{
		k1=getk(s[i-1],s[i]);k2=getk(s[i],s[i+1]);k=get(s[i]);
		ans=min(ans,calc(s[i],k1));
		if (k<=k1&&k>=k2) ans=min(ans,calc(s[i],k));
	}
	printf("%.4lf\n",ans);
	return 0;
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值