[二分答案 DP检验 几何] BalkanOI 2017. Cruise

来源:互联网 发布:星际争霸2和lol知乎 编辑:程序博客网 时间:2024/06/08 03:15

题意是给定平面内2000给点(都在y轴右边),要选出一些点,和原点组成多边形,求在多边形内的点的点权和除以多边形周长的最大值。

分数规划,二分答案

widisi,jmid

wmiddisi,j0

这样求wmiddisi,j的最大值就行了

发现答案肯定是个凸多边形

考虑DP,把点按照极角排序,令 fi 为前 i 个点选出一些点的最大值。

那么 fi=max{fj+Query(j,i)x×dist(i,j)}

其中 Query(i,j) 表示原点 ij 形成的三角形包住的点的点权和,dist 表示距离

Query(i,j) 这个东西预处理一下,就可以 n2 DP了

#include <cstdio>#include <iostream>#include <algorithm>#include <set>#include <cmath>#include <assert.h>using namespace std;const int N=2010;const double eps=1e-8;int n;double f[N];struct Pt{    int x,y,g,w; double val;    Pt(int _a=0,int _b=0):x(_a),y(_b){}    friend bool operator <(Pt a,Pt b){        return b.val>a.val;    }    friend Pt operator -(Pt a,Pt b){        return Pt(a.x-b.x,a.y-b.y);    }    double len(){        return sqrt(x*x+y*y);    }}p[N],tmp[N];inline double ang(Pt a,Pt b){    return acos((a.x*b.x+a.y*b.y)/a.len()/b.len());}inline double dist(Pt a,Pt b){    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));}int pos[N],b[N],g[N][N];inline int Query(int x,int n){    int ret=0;    for(;x<=n;x+=x&-x) ret+=b[x];    return ret;}inline void Add(int x,int y){    for(;x;x-=x&-x) b[x]+=y;}inline bool check(double x){    for(int i=1;i<=n;i++) f[i]=p[i].w-dist(p[i],Pt())*x;    for(int i=2;i<=n;i++)        for(int j=1;j<i;j++){            f[i]=max(f[i],f[j]+g[j][i]-x*dist(p[i],p[j]));        }    for(int i=1;i<=n;i++) if(f[i]-x*dist(p[i],Pt())>=0) return true;    return false;}inline void Pre(){    for(int i=1;i<=n;i++){        int cnt=0,tot=0;        for(int j=i+1;j<=n;j++){            tmp[++cnt]=p[j];            tmp[cnt].val=ang(p[j]-p[i],p[i]);            tmp[cnt].g=j;        }        sort(tmp+1,tmp+1+cnt);        for(int j=1;j<=cnt;j++){            pos[tmp[j].g]=j; b[j]=0;        }        for(int j=i+1;j<=n;j++){            Add(pos[j],p[j].w);            g[i][j]=Query(pos[j],cnt);        }    }}int main(){    scanf("%d",&n);    for(int i=1;i<=n;i++)        scanf("%d%d%d",&p[i].x,&p[i].y,&p[i].w),p[i].val=ang(p[i],Pt(0,-1));    sort(p+1,p+1+n);    Pre();    double L=0,R=1e9,mid;    while(R-L>eps){        check(mid=(L+R)/2)?L=mid:R=mid;    }    printf("%.8lf\n",R);    return 0;}