1. 程式人生 > >2018.10.15 bzoj4570: [Scoi2016]妖怪(凸包)

2018.10.15 bzoj4570: [Scoi2016]妖怪(凸包)

傳送門 不得不說這題有點東西啊。

看到題第一眼二分,用二次函式求範圍來進行checkcheck,20分滾粗了233. 於是開始思考正解。 發現可以把每隻怪物的二元組屬性看成二維座標。 這時對於一隻怪物(x,y)(x,y),一種環境相當於是一條過了點(x,y)(x,y)的直線,貢獻就是在橫縱座標的截距之和。 觀察之後很容易發現答案只跟所有點的右上凸殼有關係。 於是我們維護所有點的上凸殼。 然後依次找每個點對答案的貢獻就行了。 程式碼:

#include<bits/stdc++.h>
#define db double
#define N 1000005
using namespace
std; inline int read(){ int ans=0; char ch=getchar(); while(!isdigit(ch))ch=getchar(); while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar(); return ans; } int n,q[N],top=0; struct pot{db x,y;}p[N]; const double inf=2e9,eps=1e-10; inline bool cmp(const pot&a,const pot&b)
{return a.x==b.x?a.y<b.y:a.x<b.x;} inline double calc(const pot&a,double k){return a.x+a.y+a.x*k+a.y/k;} inline double slope(const pot&a,const pot&b){return (a.y-b.y)/(a.x-b.x);} inline pot operator-(const pot&a,const pot&b){return (pot){a.x-b.x,a.y-b.y};} inline double operator
^(const pot&a,const pot&b){return a.x*b.y-a.y*b.x;} inline double solve(){ double ret=inf,l=-inf,r,k; for(int i=1;i<=top;++i){ r=l,l=i==top?-eps:slope(p[q[i]],p[q[i+1]]),k=-sqrt(p[q[i]].y/p[q[i]].x); if(l<=k&&k<=r)ret=min(ret,calc(p[q[i]],-k)); else ret=min(ret,calc(p[q[i]],-l)),ret=min(ret,calc(p[q[i]],-r)); } return ret; } int main(){ n=read(); for(int i=1;i<=n;++i)p[i].x=read(),p[i].y=read(); sort(p+1,p+n+1,cmp); for(int i=n;i;--i){ while(top>2&&((p[i]-p[q[top-1]])^(p[q[top]]-p[q[top-1]]))>=0)--top; q[++top]=i; } while(top>1&&p[q[top]].y<=p[q[top-1]].y)--top; printf("%.4lf",solve()); return 0; }