bzoj4570: [Scoi2016]妖怪
只会瞎那啥三分,然后正解是凸包。
式子摆出来,发现可以看成二维平面上一堆斜率为-a/b的点的横纵截距和。
然后一坨平行线中,值最大的就是最右上的直线。
一个点何时会成为最大值呢,维护一个右上凸包即可。
然后对于凸包上每个点计算答案,取min。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//Achen
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<vector>
#include<queue>
#include<ctime>
#include<cmath>
const int N=1e6+7;
typedef double db;
using namespace std;
int n,que[N],top;
template<typename T> void read(T &x) {
char ch=getchar(); x=0; T f=1;
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') f=-1,ch=getchar();
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
}
struct node {
int x,y;
friend bool operator <(const node&A,const node&B) {
return A.x<B.x||(A.x==B.x&&A.y<B.y);
}
}p[N];
double get_xl(int i,int j) {
if(j==0) return 0;
if(p[i].x==p[j].x) return 0;
return ((db)p[i].y-p[j].y)/(((db)p[i].x-p[j].x)*1.0);
}
int main() {
read(n);
for(int i=1;i<=n;i++) {
read(p[i].x); read(p[i].y);
}
sort(p+1,p+n+1);
for(int i=1;i<=n;i++) {
while(top&&(get_xl(i,que[top])>=0||(top>1&&get_xl(i,que[top])>=get_xl(que[top],que[top-1]))))
top--;
que[++top]=i;
}
db l,ans;
for(int i=top;i>=1;i--) {
int a=que[i],b=que[i-1];
db r=get_xl(a,b);
db mk=sqrt((db)p[a].y/((db)p[a].x*1.0));
if(i==top) {
if(-mk<=r) ans=2.0*sqrt(p[a].x*p[a].y)+p[a].x+p[a].y;
else ans=-(r*p[a].x+(db)p[a].y/(r*1.0))+p[a].x+p[a].y;
}
else {
if((-mk>=l&&-mk<=r)) ans=min(ans,2.0*sqrt(p[a].x*p[a].y)+p[a].x+p[a].y);
else {
if(r) ans=min(ans,-(r*p[a].x+(db)p[a].y/(r*1.0))+p[a].x+p[a].y);
ans=min(ans,-(l*p[a].x+(db)p[a].y/(l*1.0))+p[a].x+p[a].y);
}
}
l=r;
}
printf("%.4lf\n",ans);
return 0;
}
/*
3
2 5
3 4
4 1
*/