[SCOI2016] 妖怪 题解

突然发现我的凸包学的有点假……


考虑每个妖怪的答案肯定是 \(at_i+dn_i+at_i\times\frac ba+dn_i\times\frac ab\)。设 \(c=\frac ba>0\),令 \(at_i>at_j\),则有:

\[\begin{aligned} (c+1)at_i+(1+\frac 1c)dn_i&>(c+1)at_j+(1+\frac 1c)dn_j\\ (c+1)(at_i-at_j)&<(1+\frac 1c)(dn_i-dn_j)\\ \frac{dn_i-dn_j}{at_i-at_j}&>-c\\ \end{aligned}\]

一个上凸包的形式,建立一个单调不升的上凸包,然后对每个点枚举 \(-c\) 即可。枚举方式可以二分,但是考虑到形如 \(ax+\frac bx\) 的单峰函数,可以通过求导求得当其取得最小值时,\(x=\sqrt\dfrac ba\),所以我们可以 \(O(1)\) 求最小值。

时间复杂度 \(O(n\log n)\)

#include<bits/stdc++.h>
#define int long long
#define xc(c,d) (dt[c].x-dt[d].x)
#define yc(c,d) (dt[c].y-dt[d].y)
#define chk(x,y,z) yc(x,y)*xc(y,z)<xc(x,y)*yc(y,z)
using namespace std;
const int N=1e6+5;
struct dot{int x,y;}dt[N];
int n,st[N],tp;double ans=1e18,lf,rt=-1e-15,mn;
int cmp(dot x,dot y){return x.x==y.x?x.y<y.y:x.x<y.x;}
double as(int i,double db){
	return dt[st[i]].x*(1-db)+dt[st[i]].y*(1-1/db);
}signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0),cin>>n;
	for(int i=1;i<=n;i++) cin>>dt[i].x>>dt[i].y;
	sort(dt+1,dt+n+1,cmp);
	for(int i=1;i<=n;st[++tp]=i++)
		while((tp>1&&chk(st[tp-1],st[tp],i))||(yc(i,st[tp])>=0&&tp)) tp--;
	for(int i=1;i<=tp;i++){
		mn=-sqrt(1.0*dt[st[i]].y/dt[st[i]].x),lf=rt;
		rt=(i==tp?-1e18:1.0*yc(st[i+1],st[i])/xc(st[i+1],st[i]));
		if(rt<=mn&&mn<=lf) ans=min(ans,as(i,mn));
		else ans=min({ans,as(i,lf),as(i,rt)});
	}return cout<<fixed<<setprecision(4)<<ans,0;
}
posted @ 2025-05-03 17:04  长安一片月_22  阅读(16)  评论(0)    收藏  举报