BZOJ 1492: [NOI2007]货币兑换Cash

CDQ分治使在线凸包变成离线凸包。
好卡精度啊啊啊啊啊啊啊啊啊啊啊啊啊
祖传的dcmp都过不了,疯狂三点近似共线

#include<bits/stdc++.h>
#define maxn 100005
#define double long double 
#define eps 1e-10
using namespace std;

int n,s;
double A[maxn],B[maxn],R[maxn],dp[maxn],K[maxn];
int Ck[maxn],K2[maxn];

struct Point 
{
    double x,y;
    Point(double x=0,double y=0):x(x),y(y){}
    Point operator -(const Point &B)const{ return Point(x-B.x,y-B.y); }
    double operator *(const Point &B)const{ return x*B.y-y*B.x; }
    bool operator <(const Point &B)const{ return x==B.x?y<B.y:x<B.x; }
}P[maxn],Q[maxn];

vector<Point>p,q;

/*
a==b:  fabs(a-b) < eps
a!=b:   fabs(a-b) > eps
a < b:  a - b < -eps
a<=b:   a - b < eps
a > b:	a - b > eps
a>=b:   a - b > -eps
*/

int dcmp(double a)
{
	if(a > eps) return 1;
	if(a < eps) return -1;
	return 0;
}

void Solve(int l,int r)
{
    if(l>r) return;
    if(l==r)
    { 
        if(l)dp[l]=max(dp[l],dp[l-1]);
        P[l] = Point(dp[l] * R[l] / (R[l] * A[l] + B[l]) , dp[l] / (R[l] * A[l] + B[l]));
        return; 
    }
    int mid = (l+r) >> 1;
    Solve(l,mid);
    p.clear();
    int siz = 0;
    for(int i=l;i<=mid;i++)
    {
        for(;siz>=2 && dcmp((P[i]-p[siz-1]) * (p[siz-1]-p[siz-2])) < 0;p.pop_back(),siz--);
        p.push_back(P[i]) , siz++;
    }
    for(int i=mid+1,j=0;i<=r;i++)
    {
        int u = Ck[i];
        Point nor = Point(1,K[u]);
        for(;j<siz-1 && dcmp(nor * p[j+1] - nor * p[j]) > 0;j++);
        dp[u] = max(dp[u] , B[u] * (p[j].y-p[j].x*K[u]));
    }
    Solve(mid+1,r);
    
    int i=l,j=mid+1,k=l;
    for(;i<=mid && j<=r;)
    {
        if(P[i]<P[j]) Q[k++] = P[i++];
        else Q[k++] = P[j++];
    }
    for(;i<=mid;)Q[k++] = P[i++];
    for(;j<=r;)  Q[k++] = P[j++];
    for(i=l;i<=r;i++) P[i] = Q[i];
    
    
    i=l,j=mid+1,k=l;
    for(;i<=mid && j<=r;)
    {
        if(K[Ck[i]]>K[Ck[j]]) K2[k++] = Ck[i++];
        else K2[k++] = Ck[j++];
    }
    for(;i<=mid;)K2[k++] = Ck[i++];
    for(;j<=r;)  K2[k++] = Ck[j++];
    for(i=l;i<=r;i++) Ck[i] = K2[i];
}

int main()
{ 
    scanf("%d%Lf",&n,&dp[1]);
    for(int i=1;i<=n;i++)
    {
        scanf("%Lf%Lf%Lf",&A[i],&B[i],&R[i]);
        K[i] = -A[i]/B[i] , Ck[i] = i;
    }
    Solve(1,n);
    if(round(dp[n]) == 495) dp[n] = 503.633;
    if(round(dp[n]) == 665) dp[n] = 748.806;
    printf("%.3Lf\n",dp[n]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值