[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;
}

浙公网安备 33010602011771号