[BZOJ1492][NOI2007]cash-[cdq分治]

Description

传送门

Solution

首先,最优情况一定是某一天把所有金券卖出或买入是最优的。

在金券一定的情况下,分散卖一定没有统一在最优的那天卖更优。

然后,我们假定在某一天卖,则在该天前面一定会有一天的全部买入能够使价值最大。

定义ans[i]为第i天能拥有的最大钱数。

则第i天能够有的A金券数为x[i]=ans[i]/(a[i]*rate[i]+b[i])*rate[i],y[i]=ans[i]/(a[i]*rate[i]+b[i])。

ans[i]=max{x[j]*a[i]+y[j]*b[i],ans[i-1]}。(ans[i-1]是假如说该天最优是某一天往后的不买也不卖的情况,因为有时候并不是一定要买卖才是最优的。)

y[j]*b[i]=ans[i]-x[j]*a[i]

y[j]=x[j]*(-a[i]/b[i])+ans[i]/b[i]。

则我们要ans[i]最大,实际上就是在j<i的每一对(x[j],y[j])上画k=-a[i]/b[i]的直线,找最大的截距。这个一看就是凸包的套路。

但是关键是,(x[j],y[j])和直线k的斜率都是没有规律的(即没有递增或递减的情况),直接处理很麻烦。

我们考虑cdq分治。(一天为一个操作,一个操作里包含了该天的信息)

初始情况下,将所有的操作按斜率k来排序。当某一个子问题处理完毕后,将子问题内的所有操作按x坐标排序。这样,当我们分治处理时,[l,mid]内的所有操作都按x排序,[mid+1,r]内的所有操作都按斜率k排序了,当然也必须保证[l,mid]的操作顺序(即第几天)在[mid+1,r]之前。

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const double eps=1e-9;
int n;

double ans[100010];
struct node{double a,b,k,rate,x,y;int id;
}w[100010],q[100010];
int st[100010];


double K(int x,int y)
{if (!y) return -1e20;
if (fabs(w[x].x-w[y].x)<eps) return 1e20;
return (w[y].y-w[x].y)/(w[y].x-w[x].x);}

bool cmp1(node a,node b){return a.k<b.k;}
bool cmp2(node a,node b)
{return a.x<b.x;}//?

void cdq(int l,int r)
{
    if (l==r)
    {
        ans[l]=max(ans[l],ans[l-1]);
        w[l].y=ans[l]/(w[l].a*w[l].rate+w[l].b);
        w[l].x=w[l].y*w[l].rate;
        return;
    }
    int mid=(l+r)/2,js0=l,js1=mid+1,tp=0;
    for (int i=l;i<=r;i++) 
        if (w[i].id<=mid) q[js0++]=w[i];else q[js1++]=w[i];
    for (int i=l;i<=r;i++) w[i]=q[i];
    
    cdq(l,mid);
    for (int i=l;i<=mid;i++) 
    {
        while (tp>1&&K(st[tp],st[tp-1])<K(i,st[tp-1])+eps) tp--;
        st[++tp]=i;
    }
    
    int j=1;
    for (int i=mid+1;i<=r;i++)
    {
        while (tp>1&&w[i].k>K(st[tp],st[tp-1])-eps) tp--;
        ans[w[i].id]=max(ans[w[i].id],w[st[tp]].x*w[i].a+w[st[tp]].y*w[i].b);
    }
    
    cdq(mid+1,r);js0=l;js1=mid+1;
    merge(w+l,w+mid+1,w+mid+1,w+r+1,q+l,cmp2);
    for (int i=l;i<=r;i++) w[i]=q[i];
}
int main()
{
    scanf("%d%lf",&n,&ans[0]);
    for (int i=1;i<=n;i++) 
    {
        scanf("%lf%lf%lf",&w[i].a,&w[i].b,&w[i].rate);w[i].id=i;
        w[i].k=-1.0*w[i].a/w[i].b;
    }
    sort(w+1,w+n+1,cmp1);
    cdq(1,n);
    printf("%.3f",ans[n]);
}

 

posted @ 2018-08-26 09:47  _雨后阳光  阅读(143)  评论(0编辑  收藏  举报