传送门

题意

初始时你有 $ s $ 元,接下来有 $ n $ 天。

在第 $ i $ 天,A券的价值为 $ A[i] $ ,B券的价值为 $ B[i] $ 。

在第 $ i $ 天,你可以进行两种操作:

  • 卖出:将 $ %OP $ 的A券和 $ %OP $ 的B券兑换成人民币,其中 $ OP $ 为 $ [0,100] $ 之间的任意实数
  • 买入:支付 $ IP $ 元,买入A、B券的总价值为 $ IP $ 元,且买入A、B券的数量之比为 $ Rate[i] $

人民币和金券的数量可以为一个实数。一天可以进行多次操作。

问你 $ n $ 天后最多能获得多少元钱。

$ (n \leq 10^5, 0 \leq A[i],B[i] \leq 10, 0 \leq Rate[i] \leq 100, ans \leq 10^9) $

题解

首先有一个显然的结论:必定有一种最优方案,满足每一天至多进行一种操作。

设 $ f[i] $ 表示在第 $ i $ 天能够获得的最大钱数,$ x[i], y[i] $ 分别表示第 $ i $ 天能够获得A、B券的最大数量。

则有:

\[x[i] = \frac{f[i]}{A[i]+B[i]/Rate[i]}
\]

\[y[i] = \frac{f[i]}{A[i]*Rate[i]+B[i]}
\]

然后考虑 $ f[i] $ 如何转移。

对于第 $ i $ 天来说,要么什么都不做,要么将之前某一天的金券在这一天卖掉。

所以:

\[f[i] = max(f[i-1], x[j]*A[i]+y[j]*B[i]) \quad (1 \leq j<i)
\]

然而朴素dp是 $ O(n^2) $ 的过不了……

先让所有的 $ f[i] = max(f[i],f[i-1]) $

那么剩下的就是 $ f[i] = max(x[j] \ast A[i]+y[j] \ast B[i]) \quad (1 \leq j<i) $

变形得:

\[y[j] = -\frac{A[i]}{B[i]}*x[j] + \frac{f[i]}{B[i]}
\]

上式是一条斜率为 $ -\dfrac{A[i]}{B[i]} ​$ 且过点 $ (x[j], y[j]) ​$ 的直线的斜截式。

由于我们想让 $ f[i] $ 最大,并且因为 $ B[i] \geq 0 $ ,所以只要让上式的截距 $ \dfrac{f[i]}{B[i]} $ 最大即可。

我们考虑用cdq转移dp。

首先对于当前区间,按照 $ id $ 大小分成两个区间。$ id $ 小的在左区间,否则在右区间,且分别在左右区间中 $ id $ 大小递增(满足了 $ id $ 小的更新 $ id $ 大的的答案)。

假设左边递归处理后是以 $ x,y $ 分别为第一和第二关键字从小到大排好序的,右边提前按照斜率 $ -\dfrac{A[i]}{B[i]} $ 从大到小排好序的。

对于左区间,我们可以先for循环扫一遍左区间求出一个上凸壳。

那么右区间每个元素的答案,一定是取它与左边凸壳相切时的截距(因为要让截距最大)。

并且在依次求出右区间答案时,切点是不断向右移动的(因为右区间中元素的斜率不断减小)。

所以总复杂度就降为了 $ O(nlogn) $

AC Code

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#define MAX_N 100005
#define INF_LF 1e13
#define EPS 1e-8 using namespace std; struct Data
{
double a,b,r,x,y,k;
int id;
friend bool operator < (const Data &a,const Data &b)
{
return a.k>b.k;
}
}; int n;
double f[MAX_N];
Data t[MAX_N];
Data c[MAX_N];
Data tmp[MAX_N]; inline double fabs(double x)
{
return x>0 ? x : -x;
} inline bool eq(double x,double y)
{
return fabs(x-y)<=EPS;
} inline double slope(Data a,Data b)
{
if(eq(a.x,b.x)) return INF_LF;
return (a.y-b.y)/(a.x-b.x);
} inline bool cmp(Data a,Data b)
{
if(a.x!=b.x) return a.x<b.x;
return a.y<b.y;
} void read()
{
scanf("%d%lf",&n,&f[1]);
for(int i=1;i<=n;i++)
{
scanf("%lf%lf%lf",&t[i].a,&t[i].b,&t[i].r);
t[i].k=(eq(t[i].b,0) ? -INF_LF : -t[i].a/t[i].b);
t[i].id=i;
}
} void cdq(int l,int r)
{
if(l==r)
{
int id=t[l].id;
f[id]=max(f[id],f[id-1]);
t[l].x=f[id]/(t[l].a+t[l].b/t[l].r);
t[l].y=f[id]/(t[l].a*t[l].r+t[l].b);
return;
}
int mid=(l+r)>>1,tot=0;
for(int i=l,j=mid+1,k=l;k<=r;k++)
{
if(t[k].id<=mid) tmp[i++]=t[k];
else tmp[j++]=t[k];
}
for(int i=l;i<=r;i++) t[i]=tmp[i];
cdq(l,mid);
for(int i=l;i<=mid;i++)
{
while(tot>=2 && slope(c[tot-1],c[tot])<slope(c[tot],t[i])) tot--;
c[++tot]=t[i];
}
int p=1;
for(int i=mid+1;i<=r;i++)
{
while(p<tot && slope(c[p],c[p+1])>t[i].k) p++;
f[t[i].id]=max(f[t[i].id],c[p].x*t[i].a+c[p].y*t[i].b);
}
cdq(mid+1,r);
for(int i=l,j=mid+1,k=l;k<=r;k++)
{
if((i<=mid && cmp(t[i],t[j])) || j>r) tmp[k]=t[i++];
else tmp[k]=t[j++];
}
for(int i=l;i<=r;i++) t[i]=tmp[i];
} void work()
{
sort(t+1,t+1+n);
cdq(1,n);
printf("%.3f\n",f[n]);
} int main()
{
read();
work();
}

BZOJ 1492 [NOI2007]货币兑换Cash:斜率优化dp + cdq分治的更多相关文章

  1. P4027 [NOI2007]货币兑换(斜率优化dp+cdq分治)

    P4027 [NOI2007]货币兑换 显然,如果某一天要买券,一定是把钱全部花掉.否则不是最优(攒着干啥) 我们设$f[j]$为第$j$天时用户手上最多有多少钱 设$w$为花完钱买到的$B$券数 $ ...

  2. BZOJ 1492: [NOI2007]货币兑换Cash 斜率优化 + splay动态维护凸包

    Description 小Y最近在一家金券交易所工作.该金券交易所只发行交易两种金券:A纪念券(以下简称A券)和 B纪念券(以下 简称B券).每个持有金券的顾客都有一个自己的帐户.金券的数目可以是一个 ...

  3. 【BZOJ 1492】 [NOI2007]货币兑换Cash 斜率优化DP

    先说一下斜率优化:这是一种经典的dp优化,是OI中利用数形结合的思想解决问题的典范,通常用于优化dp,有时候其他的一些决策优化也会用到,看待他的角度一般有两种,但均将决策看为二维坐标系上的点,并转化为 ...

  4. 【bzoj3672】[Noi2014]购票 斜率优化dp+CDQ分治+树的点分治

    题目描述  给出一棵以1为根的带边权有根树,对于每个根节点以外的点$v$,如果它与其某个祖先$a$的距离$d$不超过$l_v$,则可以花费$p_vd+q_v$的代价从$v$到$a$.问从每个点到1花费 ...

  5. BZOJ 1492: [NOI2007]货币兑换Cash( dp + 平衡树 )

    dp(i) = max(dp(i-1), x[j]*a[i]+y[j]*b[i]), 0<j<i. x, y表示某天拥有的最多钱去买金券, 金券a和金券b的数量. 然后就很明显了...平衡 ...

  6. [BZOJ1492][NOI2007]货币兑换Cash(斜率优化+CDQ分治)

    1492: [NOI2007]货币兑换Cash Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 5838  Solved: 2345[Submit][Sta ...

  7. [BZOJ1492] [NOI2007]货币兑换Cash 斜率优化+cdq/平衡树维护凸包

    1492: [NOI2007]货币兑换Cash Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 5907  Solved: 2377[Submit][Sta ...

  8. 【BZOJ1492】[NOI2007]货币兑换Cash 斜率优化+cdq分治

    [BZOJ10492][NOI2007]货币兑换Cash Description 小Y最近在一家金券交易所工作.该金券交易所只发行交易两种金券:A纪念券(以下简称A券)和 B纪念券(以下简称B券).每 ...

  9. ●BZOJ 1492 [NOI2007]货币兑换Cash

    题链: http://www.lydsy.com/JudgeOnline/problem.php?id=1492 题解: 斜率优化DP,CDQ分治 定义$DP[i]$为第i天结束后的最大收益. 由于题 ...

  10. BZOJ 1492: [NOI2007]货币兑换Cash [CDQ分治 斜率优化DP]

    传送门 题意:不想写... 扔链接就跑 好吧我回来了 首先发现每次兑换一定是全部兑换,因为你兑换说明有利可图,是为了后面的某一天两种卷的汇率差别明显而兑换 那么一定拿全利啊,一定比多天的组合好 $f[ ...

随机推荐

  1. 巨蟒python全栈开发flask2

    内容回顾: 上节回顾: Flask .response 三剑客: render_template 模板 redirect 重定向 - URL地址 "" 字符串 HTTPRespon ...

  2. contos7 mongodb安装教程

    通过yum安装mongodb 1.创建文件mongodb.repo文件, cd /etc/yum.repos.d/ vi mongodb.repo 复制如下代码: [mongodb-org-3.4] ...

  3. <2014 05 14> Android平台下2D/3D开发攻略

    Android通过OpenGL包含了对高性能2D和3D图形的支持,尤其支持OpenGLES API.OpenGL是一个跨平台的图形API,提供了软件操作3D图形硬件的接口.OpenGLES是一个专用于 ...

  4. javascript自定义属性的应用

    你知道吗?JavaScript可以为任何HTML元素添加任意的自定义属性,而且你可能无意中已经使用过自定义属性了,那么自定义属性通常有哪些应用呢? 1.想用“匹配”.对应关系的时候就用索引值 2.同时 ...

  5. iptables练习题(四)

    设有一台Linux服务器,利用iptables作为防火墙,要求新建一条名为MYCHAIN的新链,来实现只允许开放本机的http服务,其余协议和端口均拒绝. 脚本: [root@miyan ~]# ca ...

  6. 线性表 - C语言完整实现

    #include <stdio.h> #define false 0 #define true 1 #define MAXSIZE 20 typedef int bool; typedef ...

  7. 使用npm构建前端项目基本流程

    现在各种前端框架, 库文件基本都托管到npm上, 我们平常下载到别人的项目文件, 也基本是用npm 构建的, 不了解点node和npm那是寸步难行. 下面介绍的代码示例不敢说是最佳实践, 但都是我亲自 ...

  8. MySQL 数据类型(Day41)

    一.介绍 存储引擎决定了表的类型,而表内存放的数据也要有不同的类型,每种数据类型都有自己的高度,但宽度是可选的. mysql数据类型概览 #1.数字:(默认都是有符号,宽度指的是显示宽度,与存储无关) ...

  9. PL/SQL编程—包

    1.PLSQL 中的包就相当于java中的package,主要好处有(1)防止命名污染,(2)功能统一,(3)允许重载,(4)可以隐藏核心代码,(5)最重要的就是断开依赖链. 2.对于一个程序需要大量 ...

  10. $用python-docx模块读写word文档

    工作中会遇到需要读取一个有几百页的word文档并从中整理出一些信息的需求,比如产品的API文档一般是word格式的.几百页的文档,如果手工一个个去处理,几乎是不可能的事情.这时就要找一个库写脚本去实现 ...