bzoj4570: [Scoi2016]妖怪

传送门

只会瞎那啥三分,然后正解是凸包。

题解传送门

式子摆出来,发现可以看成二维平面上一堆斜率为-a/b的点的横纵截距和。

然后一坨平行线中,值最大的就是最右上的直线。

一个点何时会成为最大值呢,维护一个右上凸包即可。

然后对于凸包上每个点计算答案,取min。

//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
*/
View Code

 

posted @ 2018-01-30 16:46  啊宸  阅读(194)  评论(0编辑  收藏  举报