BZOJ4570 [Scoi2016]妖怪

16 篇文章 0 订阅
3 篇文章 0 订阅

每个妖怪在指定环境下攻击和防御的变化量比值是一个定值,所以我们可以把妖怪看成平面上的点,横纵坐标为攻击和防御,对于每个妖怪环境看成一条k=-b/a的过该妖怪直线,每个妖怪的战斗力就是x=0时y的值加上y=0时x的值

所以我们要维护一个右上凸壳,用一条直线卡这个凸壳,假设卡到的妖怪是(x,y),我们可以用x+y-kx-y/k更新答案

对于每个单独妖怪,战斗力=x+y-kx-y/k,对勾函数,当k=-sqrt(y/x)时取得最小值

维护一个右上凸壳,对于每个凸壳上的妖怪,用他和他上一个妖怪连成的直线更新答案,然后算出这个妖怪的最优斜率,如果这个斜率的直线卡在凸包上的妖怪是这个妖怪则更新答案

复杂度O(n logn)

如果二分写的很优的话也可以过

设t=b/a

二分答案c,如果存在一个t使得对于所有妖怪x+y+tx+y/t<=c有解则c可行

等式两边同时乘以x,变为二次函数,相当于数轴上线段判有没有交,每次可以O(n)判断

复杂度O(n logINF/eps)反正我是特勒了

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<iomanip>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<bitset>
using namespace std;
#define MAXN 1000010
#define MAXM 1010
#define ll long long
#define INF 1000000000
#define MOD 1000000007
#define eps 1e-8
char xB[1<<15],*xS=xB,*xT=xB;
#define getc() (xS==xT&&(xT=(xS=xB)+fread(xB,1,1<<15,stdin),xS==xT)?0:*xS++)
inline int read(){
    char ch=getc();
    int f=1,x=0;
    while(!(ch>='0'&&ch<='9')){if(ch=='-')f=-1;ch=getc();}
    while(ch>='0'&&ch<='9'){x=x*10+(ch-'0');ch=getc();}
    return x*f;
}
struct pt{
	int x;
	int y;
	pt(){
		
	}
	pt(int _x,int _y){
		x=_x;
		y=_y;
	}
	friend pt operator -(pt x,pt y){
		return pt(x.x-y.x,x.y-y.y);
	}
	friend double operator *(pt x,pt y){
		return 1.0*x.x*y.y-1.0*x.y*y.x;
	}
	friend bool operator <(pt x,pt y){
		return x.x!=y.x?x.x<y.x:x.y>y.y;
	}
};
int n;
pt a[MAXN];
pt s[MAXN];
int tp;
double ans=1e10;
double k(pt &x){
	return -sqrt(1.0*x.y/x.x);
}
double xl(pt &x,pt &y){
	return x.x==y.x?-1e60:1.0*(x.y-y.y)/(x.x-y.x);
}
double cal(pt x,double k){
	if(fabs(k)<eps){
		return 1e20;
	}
	return 1.0*x.x+x.y-1.0*x.x*k-1.0*x.y/k;
}
int main(){
	int i;
	n=read();
	for(i=1;i<=n;i++){
		a[i].x=read();
		a[i].y=read();
	}
	sort(a+1,a+n+1);
	for(i=1;i<=n;i++){
		while(tp>=2&&(s[tp]-s[tp-1])*(a[i]-s[tp])>0){
			tp--;
		}
		s[++tp]=a[i];
	}
	double K;
	if(tp!=1){
		K=k(s[1]);
		if(K>=xl(s[1],s[2])){
			ans=min(ans,cal(s[1],K));
		}
		K=k(s[tp]);
		if(K<=xl(s[tp-1],s[tp])){
			ans=min(ans,cal(s[tp],K));
		}
		ans=min(ans,cal(s[tp],xl(s[tp-1],s[tp])));
	}else{
		ans=cal(s[1],k(s[1]));
	}
	for(i=2;i<tp;i++){
		K=k(s[i]);
		if(K<=xl(s[i-1],s[i])&&K>=xl(s[i],s[i+1])){
			ans=min(ans,cal(s[i],K));
		}
		ans=min(ans,cal(s[i],xl(s[i-1],s[i])));
	}
	printf("%.4lf\n",ans);
	return 0;
}

/*

*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值