P4027 [NOI2007] 货币兑换

题目描述

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

每天随着市场的起伏波动,两种金券都有自己当时的价值,即每一单位金券当天可以兑换的人民币数目。我们记录第 K K K 天中 A 券和 B 券的价值分别为 A K A_K AK​ 和 B K B_K BK​(元/单位金券)。

为了方便顾客,金券交易所提供了一种非常方便的交易方式:比例交易法。

比例交易法分为两个方面:

a) 卖出金券:顾客提供一个 [ 0 , 100 ] [0, 100] [0,100] 内的实数 O P OP OP 作为卖出比例,其意义为:将 O P % OP\% OP% 的 A 券和 O P % OP\% OP% 的 B 券以当时的价值兑换为人民币;

b) 买入金券:顾客支付 I P IP IP 元人民币,交易所将会兑换给用户总价值为 I P IP IP 的金券,并且,满足提供给顾客的 A 券和 B 券的比例在第 K K K 天恰好为 R a t e K \mathrm{Rate}_ K RateK​;

例如,假定接下来 3 3 3 天内的 A K , B K , R a t e K A_K,B_K,\mathrm{Rate}_ K AK​,BK​,RateK​ 的变化分别为:

时间 A K A_K AK​ B K B_K BK​ R a t e K \mathrm{Rate}_ K RateK​
第一天 1 1 1 1 1 1 1 1 1
第二天 1 1 1 2 2 2 2 2 2
第三天 2 2 2 2 2 2 3 3 3

假定在第一天时,用户手中有 100 100 100 元人民币但是没有任何金券。

用户可以执行以下的操作:

时间 用户操作 人民币(元) A 券的数量 B 券的数量
开户 100 100 100 0 0 0 0 0 0
第一天 买入 100 100 100 元 0 0 0 50 50 50 50 50 50
第二天 卖出 50 % 50\% 50% 75 75 75 25 25 25 25 25 25
第二天 买入 60 60 60 元 15 15 15 55 55 55 40 40 40
第三天 卖出 100 % 100\% 100% 205 205 205 0 0 0 0 0 0

注意到,同一天内可以进行多次操作。

小 Y 是一个很有经济头脑的员工,通过较长时间的运作和行情测算,他已经知道了未来 N N N 天内的 A 券和 B 券的价值以及 R a t e \mathrm{Rate} Rate。他还希望能够计算出来,如果开始时拥有 S S S 元钱,那么 N N N 天后最多能够获得多少元钱。

输入格式

第一行两个正整数 N , S N,S N,S,分别表示小 Y 能预知的天数以及初始时拥有的钱数。

接下来 N N N 行,第 K K K 行三个实数 A K , B K , R a t e K A_K,B_K,\mathrm{Rate} _ K AK​,BK​,RateK​ ,意义如题目中所述。

输出格式

只有一个实数 M a x P r o f i t \mathrm{MaxProfit} MaxProfit,表示第 N N N 天的操作结束时能够获得的最大的金钱数目。答案保留 3 3 3 位小数。

样例

样例输入
3 100
1 1 1
1 2 2
2 2 3
样例输出
225.000

提示

时间 用户操作 人民币(元) A 券的数量 B 券的数量
开户 100 100 100 0 0 0 0 0 0
第一天 买入 100 100 100 元 0 0 0 50 50 50 50 50 50
第二天 卖出 100 % 100\% 100% 150 150 150 0 0 0 0 0 0
第二天 买入 150 150 150 元 0 0 0 75 75 75 37.5 37.5 37.5
第三天 卖出 100 % 100\% 100% 225 225 225 0 0 0 0 0 0

本题没有部分分,你的程序的输出只有和标准答案相差不超过 0.001 0.001 0.001 时,才能获得该测试点的满分,否则不得分。

测试数据设计使得精度误差不会超过 1 0 − 7 10^{-7} 10−7 。

对于 40 % 40\% 40% 的测试数据,满足 N ≤ 10 N \le 10 N≤10。

对于 60 % 60\% 60% 的测试数据,满足 N ≤ 1000 N \le 1 000 N≤1000。

对于 100 % 100\% 100% 的测试数据,满足 N ≤ 1 0 5 N \le 10^5 N≤105。

对于 100 % 100\% 100% 的测试数据,满足:

0 < A K ≤ 10 0 < A_K \leq 10 0<AK​≤10, 0 < B K ≤ 10 0 < B_K\le 10 0<BK​≤10, 0 < R a t e K ≤ 100 0 < \mathrm{Rate}_K \le 100 0<RateK​≤100, M a x P r o f i t ≤ 1 0 9 \mathrm{MaxProfit} \leq 10^9 MaxProfit≤109。

输入文件可能很大,请采用快速的读入方式。

必然存在一种最优的买卖方案满足:

每次买进操作使用完所有的人民币,每次卖出操作卖出所有的金券。

题解

前置知识: c d q cdq cdq分治

题目已经说了,必然存在一种最优的买卖方案满足:每次买进操作使用完所有的人民币,每次卖出操作卖出所有的金券。所以我们设 f i f_i fi​表示在第 i i i天将所有金券卖出所获得的最多的钱。设第 i i i天得到的金券数是第 j j j天买入的, x j x_j xj​表示第 j j j天A券的数量, y j y_j yj​表示第 j j j天B券的数量,则有:

x j = r j f j r j a j + b j , y j = f j r j a j + b j x_j=\frac{r_j f_j}{r_j a_j+b_j},y_j=\frac{f_j}{r_ja_j+b_j} xj​=rj​aj​+bj​rj​fj​​,yj​=rj​aj​+bj​fj​​

即可得到状态转移方程 f i = x j a i + y j b i f_i=x_ja_i+y_jb_i fi​=xj​ai​+yj​bi​,变为斜截式方程得 y j = − a i b i x j + f i b i y_j=-\frac{a_i }{b_i}x_j+\frac{f_i}{b_i} yj​=−bi​ai​​xj​+bi​fi​​

此时,对于平面上每个点 ( x , y ) (x,y) (x,y),我们只需用斜率为 − a i b i -\frac{a_i}{b_i} −bi​ai​​的直线去切其中一个点,可以使截距最大的点即为最优决策点。

至于如何求使其截距最大的点,斜率优化DP中有讲解(可看可不看,下文有讲解)

接下来就用 c d q cdq cdq分治来解决问题。对于当前区间 [ l , r ] [l,r] [l,r],将其分为左右两个区间。对左区间按 x i x_i xi​从小到大排序,对右区间按 − a i b i -\frac{a_i}{b_i} −bi​ai​​从小到大排序。用栈来维护左区间所形成的斜率单调递减的凸包,然后用右区间的点依次匹配左区间的点。对于每次匹配,如果左区间的点 i i i与左边的点连成的线斜率小于当前当前枚举到的斜率,则 i i i左边的点比 i i i更优。

因为用的是栈,所以平摊下来每次是 O ( n ) O(n) O(n)的,总时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)

code

#include<bits/stdc++.h>
using namespace std;
const double eps=1e-8,inf=1e9;
int n,h[100005];
double f[100005];
struct node{double a,b,r,x,y,k;int id;
}w[100005],w1[100005];
bool cmp1(node ax,node bx){return ax.k<bx.k;
}
bool cmp2(node ax,node bx){return ax.x<bx.x;
}
double gt(int i,int j){if(fabs(w[i].x-w[j].x)<=eps) return inf;return (w[j].y-w[i].y)/(w[j].x-w[i].x);
}
void cdq(int l,int r){if(l==r){f[l]=max(f[l],f[l-1]);w[l].y=f[l]/(w[l].a*w[l].r+w[l].b);w[l].x=w[l].y*w[l].r;return;}int mid=l+r>>1;for(int o=l,i=l-1,j=mid;o<=r;o++){if(w[o].id<=mid) w1[++i]=w[o];else w1[++j]=w[o];}for(int o=l;o<=r;o++) w[o]=w1[o];cdq(l,mid);int tp=0;for(int i=l;i<=mid;i++){while(tp>=2&&gt(h[tp],i)+eps>gt(h[tp-1],h[tp])) --tp;h[++tp]=i;}for(int i=mid+1;i<=r;i++){while(tp>=2&&gt(h[tp-1],h[tp])<=w[i].k+eps) --tp;f[w[i].id]=max(f[w[i].id],w[h[tp]].x*w[i].a+w[h[tp]].y*w[i].b);}cdq(mid+1,r);sort(w+l,w+r+1,cmp2);
}
int main()
{scanf("%d%lf",&n,&f[0]);for(int i=1;i<=n;i++){scanf("%lf%lf%lf",&w[i].a,&w[i].b,&w[i].r);w[i].k=-w[i].a/w[i].b;w[i].id=i;}sort(w+1,w+n+1,cmp1);cdq(1,n);printf("%.3f",f[n]);return 0;
}

P4027 [NOI2007] 货币兑换相关推荐

  1. bzoj千题计划237:bzoj1492: [NOI2007]货币兑换Cash

    http://www.lydsy.com/JudgeOnline/problem.php?id=1492 dp[i] 表示 第i天卖完的最大收益 朴素的dp: 枚举从哪一天买来的在第i天卖掉,或者是不 ...

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

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

  3. 【bzoj 1492】【codevs 1797】 [NOI2007]货币兑换Cash (dp+cdq分治)

    1492: [NOI2007]货币兑换Cash Time Limit: 5 Sec  Memory Limit: 64 MB Submit: 3803  Solved: 1604 [Submit][S ...

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

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

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

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

  6. NOI2007 货币兑换 - CDQ分治斜率优化dp

    斜率优化dp维护一个凸壳.如果\(x, y\)坐标都递增,可以用单调队列,如果只有\(x\)递增,可以在凸壳上二分斜率,如果\(x, y\)都不递增,则需要在凸包中插入,可以用平衡树或cdq分治维护. ...

  7. bzoj1492: [NOI2007]货币兑换Cash

    码了我两个星期的题啊,终于写完了,感觉一半的时间都在调splay,后面写cdq好像轻松一点,cdq码量小一倍,而且又好想(可能是我调的时候理解了)感觉这种黑科技很nb,关键是可以减少细节出错(像我这种 ...

  8. [NOI2007]货币兑换Cash(DP+动态凸包)

    第一次打动态凸包维护dp,感觉学到了超级多的东西. 首先,set是如此的好用!!!可以通过控制一个flag来实现两种查询,维护凸包和查找斜率k 不过就是重载运算符和一些细节方面有些恶心,90行解决 后 ...

  9. [NOI2007] 货币兑换 解题报告

    https://www.zybuluo.com/rebirth1120/note/1528243 转载于:https://www.cnblogs.com/BruceW/p/11315368.html

最新文章

  1. android兼容小米xiaomi刘海屏解决方案
  2. python 快速排序_小白入门知识详解:Python实现快速排序的方法(含实例代码)...
  3. myeclipse10.7安装git插件
  4. 集合2--毕向东java基础教程视频学习笔记
  5. Elasticsearch集群和索引常用命令
  6. Linux下串口通信详解
  7. 【C语言进阶深度学习记录】三十一 数组作为函数参数时退化为指针
  8. 用c语言程序算自己的年龄,新手编的小程序:计算年龄和出生后经历的天数的小程序...
  9. java计算时间差距_硬计算和软计算之间的差异
  10. Hibernate 原汁原味的四种抓取策略(转)
  11. Windows按键翻译
  12. 信号检测与估计c语言,清华大学出版社-图书详情-《信号检测与估计(第2版)》...
  13. Java五子棋书,本文实例讲述了java实现的五子棋游戏代码,分享给大家供大家参考,具体代码如下package gyb.exam; import java.awt.Bo...
  14. MBR15200FAC-ASEMI插件肖特基二极管MBR15200FAC
  15. MATLAB等额还款代码,matlab算法实现对等额本息和等额本金两种还款方式的计算
  16. 机房综合布线施工主要是几个方面
  17. 2023 年大淘宝 Web 端技术概览
  18. 关于网络游戏的影响(腾讯游戏)
  19. flutter 图片加载
  20. python手写数字识别实验报告_Python代码实现简单的MNIST手写数字识别(适合初学者看)...

热门文章

  1. JavaWeb之前后端分离的三步骤
  2. GBase数据库设计范式
  3. IDEA的这个bug,折磨级别五颗星
  4. vue :disabled
  5. AI老中医开源了! 一个厉害的中医GPT!
  6. 百元左右平替苹果耳机有哪些?值得入手的蓝牙耳机推荐
  7. 数据库缓存服务器——Redis(入门级)
  8. 用WebStorm搭建vue项目
  9. react网页适配不同分辨率_前端页面适应不同分辨率
  10. HCIA-第二节课学习笔记总结(ARP协议与IP地址)