【问题描述】

小 Y最近在一家金券交易所工作。该金券交易所只发行交易两种金券:A纪念券(以下简称A券)和B纪念券(以下简称B券)。每个持有金券的顾客都有一个自己的 帐户。金券的数目可以是一个实数。每天随着市场的起伏波动,两种金券都有自己当时的价值,即每一单位金券当天可以兑换的人民币数目。我们记录第K天中A券 和B券的价值分别为AK和BK(元/单位金券)。
为了方便顾客,金券交易所提供了一种非常方便的交易方式:比例交易法。
比例交易法分为两个方面:
a)卖出金券:顾客提供一个[0,100]内的实数OP作为卖出比例,其意义为:将OP%的A券和OP%的B券以当时的价值兑换为人民币;
b)买入金券:顾客支付IP元人民币,交易所将会兑换给用户总价值为IP的金券,并且,满足提供给顾客的A券和B券的比例在第K天恰好为RateK;
例如,假定接下来3天内的Ak、Bk、RateK的变化分别为:

时间 Ak Bk Ratek
第一天 1 1 1
第二天 1 2 2
第三天 2 2 3

假定在第一天时,用户手中有100元人民币但是没有任何金券。
用户可以执行以下的操作:

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

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

【输入格式】

第一行两个正整数N、S,分别表示小Y能预知的天数以及初始时拥有的钱数。接下来N行,第K行三个实数AK、BK、RateK,意义如题目中所述。

【输出格式】

只有一个实数MaxProfit,表示第N天的操作结束时能够获得的最大的金钱数目。答案保留3位小数。

-----------------------------------------------------------

正解=动归+平衡树

考虑简单Dp

   状态:设f[i]为第i天能赚的最多钱为多少

   方程:f[i]=max(f[i-1],na*A+nb*B)(j< i)

    (na,nb为第j天能换到的A劵数量和B劵数量)

考虑优化:

  设 na 为 x,nb 为 y

  则sum=x*A+y*B 既 y = -A/B*x+sum/B

  由于A,B为定值

   sum的Max以 -A/B 为斜率的过点(x,y)的截距的Max*B

  显然可能得到Max解截距的点必然是个上凸壳

    建立以x为关键字的平衡树维护值(其实很简(dan)单(teng))

由于在树上的点映射的截距具有单调性,

  找最大值是便可通过平衡树的前驱后继判断Max在左子树或右子树;

代码如下:

  

 #include<cstring>
#include<algorithm>
#include<cstdio>
#include<string>
#include<iostream>
#include<queue>
#define INF 99999999
#define LL long long
#define Cint(o) const int o=0
#define Cdou(o) const double o=0
#define Min(num1,num2) if(num1>num2) num1=num2
#define Max(num1,num2) if(num1<num2) num1=num2
struct Tree{
int l,r,f;
double x,y;
Tree(Cint(a1),Cint(a2),Cint(a3),Cdou(a4),Cdou(a5)):
l(a1),r(a2),f(a3),x(a4),y(a5){}
}a[];
int Root,Total,n;
const long double O=1e-;
void cal(double sum,double A,double B,double K,double &na,double &nb){
nb=sum/(A*K+B);
na=nb*K;
}
void rig(int now){
int f=a[now].f;
a[now].f=a[f].f;
if(a[a[f].f].l==f) a[a[f].f].l=now;
if(a[a[f].f].r==f) a[a[f].f].r=now;
a[f].f=now;
a[f].l=a[now].r;
a[a[now].r].f=f;
a[now].r=f;
} void lef(int now){
int f=a[now].f;
a[now].f=a[f].f;
if(a[a[f].f].l==f) a[a[f].f].l=now;
if(a[a[f].f].r==f) a[a[f].f].r=now;
a[f].f=now;
a[f].r=a[now].l;
a[a[now].l].f=f;
a[now].l=f;
}
double cross(Tree A,Tree B,Tree C){
return (B.x-A.x)*(C.y-A.y)-(C.x-A.x)*(B.y-A.y);
// x1 *y2-x2*y1
}
void splay(int now,int F=){
while(a[now].f!=F){
int f=a[now].f,ff=a[f].f;
if(ff==F)
if(a[f].l==now) rig(now);
else lef(now);
else
if(a[ff].l==f)
if(a[f].l==now) rig(f),rig(now);
else lef(now),rig(now);
else
if(a[f].l==now) rig(now),lef(now);
else lef(f),lef(now);
}
if(!F) Root=now;
} void creat(){
Total=;
Root=;
a[].r=;
a[].f=;
a[].x=-INF;
a[].x=INF;
}
int prev(int now){
splay(now);
now=a[now].l;
while(a[now].r) now=a[now].r;
return now;
}
int succ(int now){
splay(now);
now=a[now].r;
while(a[now].l) now=a[now].l;
return now;
}
void del(int start,int now,int end){
splay(start);
splay(end,start);
a[a[Root].r].l=;
//printf("Delte(%d)",now);
}
void maintain(int now){
int p=prev(now),s=succ(now);
if(p!=&&s!=)
if(cross(a[p],a[s],a[now])<O){
del(p,now,s);
return ;
}
while(){
if(p==) break;
int pp=prev(p);
if(pp!=&&cross(a[pp],a[now],a[p])<O)
del(pp,p,now),
p=pp;
else
break;
}
while(){
if(s==) break;
int ss=succ(s);
if(ss!=&&cross(a[now],a[ss],a[s])<O)
del(now,s,ss),
s=ss;
else
break;
}
}
void insert(double x,double y){
for(int now=Root;;){
if(a[now].x-x<O&&x-a[now].x<O){
Max(a[now].y,y);
maintain(now);
return ;
}
if(a[now].x-x>O)
if(a[now].l)
now=a[now].l;
else {
a[now].l=++Total;
a[Total].f=now;
a[Total].x=x;
a[Total].y=y;
maintain(Total);
return ;
}
else
if(a[now].r)
now=a[now].r;
else{
a[now].r=++Total;
a[Total].f=now;
a[Total].x=x;
a[Total].y=y;
maintain(Total);
return ; }
}
}
double fo(Tree now,double K){
return now.y-now.x*K;
}
int pre(int now){
//splay(now);
now=a[now].l;
while(a[now].r) now=a[now].r;
return now;
} int suc(int now){
// splay(now);
now=a[now].r;
while(a[now].l) now=a[now].l;
return now;
}
double slove(double K){
for(int now=Root;;){
if(now==){
now=suc(now);
continue ;
}
if(now==){
now=pre(now);
continue ;
} if(a[now].l){
int k=pre(now);
if(k!=&&fo(a[now],K)<fo(a[k],K)+O){
now=a[now].l;
continue ;
}
}
if(a[now].r){
int k=suc(now);
if(k!=&&fo(a[now],K)<fo(a[k],K)+O){
now=a[now].r;
continue ;
}
}
return fo(a[now],K); }
}
int main(){
double A,B,K,na,nb,s;
scanf("%d%lf",&n,&s);
scanf("%lf%lf%lf",&A,&B,&K);
creat();
cal(s,A,B,K,na,nb);
insert(na,nb);
for(int i=;i<n;i++){
scanf("%lf%lf%lf",&A,&B,&K);
double temp=slove(-A/B)*B;
Max(s,temp);
cal(s,A,B,K,na,nb);
insert(na,nb);
}
printf("%.3lf",s);
}

NOI2007 货币兑换的更多相关文章

  1. 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的数量. 然后就很明显了...平衡 ...

  2. bzoj1492[NOI2007]货币兑换Cash cdq分治+斜率优化dp

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

  3. cdq分治(hdu 5618 Jam's problem again[陌上花开]、CQOI 2011 动态逆序对、hdu 4742 Pinball Game、hdu 4456 Crowd、[HEOI2016/TJOI2016]序列、[NOI2007]货币兑换 )

    hdu 5618 Jam's problem again #include <bits/stdc++.h> #define MAXN 100010 using namespace std; ...

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

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

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

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

  6. [NOI2007]货币兑换 cdq分治,斜率优化

    [NOI2007]货币兑换 LG传送门 妥妥的\(n \log n\)cdq做法. 这题用cdq分治也可以\(n \log n\)但是在洛谷上竟然比一些优秀的splay跑得慢真是见了鬼了看来还是人丑常 ...

  7. 洛谷 P4027 [NOI2007]货币兑换 解题报告

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

  8. [NOI2007]货币兑换 --- DP + 斜率优化(CDQ分治)

    [NOI2007]货币兑换 题目描述: 小 Y 最近在一家金券交易所工作.该金券交易所只发行交易两种金券:A 纪念券(以下简称 A 券)和 B 纪念券(以下简称 B 券). 每个持有金券的顾客都有一个 ...

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

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

  10. BZOJ1492: [NOI2007]货币兑换Cash 【dp + CDQ分治】

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

随机推荐

  1. 二进制方式快速安装MySQL数据库命令集合

    二进制方式快速安装MySQL数据库命令集合 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 1.安装mysql ls mysql ...

  2. wpf采用Xps实现文档显示、套打功能(原创)

    近期的一个项目需对数据进行套打,用户要求现场不允许安装office.页面预览显示必须要与文档完全一致,xps文档来对数据进行处理.Wpf的DocumentView 控件可以直接将数据进行显示,xps也 ...

  3. php加了命名空间没引入初始化文件:类的命名空间要与文件夹名一致namespace Business\Event;缺少了Event

    php加了命名空间没引入初始化文件:类的命名空间要与文件夹名一致namespace Business\Event;缺少了Event

  4. 一个Highcharts的例子

    关键字:Highcharts <!DOCTYPE HTML> <html> <head> <meta http-equiv="Content-Typ ...

  5. Python学习_列表推导和Lambda表达式

    列表推导目的是减少将一个列表转换为另一个列表时所需编写的代码量,其功能也能用列表迭代完成 1.根据要求创建列表threes_and_fives(列表值包括1到15中能够被3或者5正常的数) three ...

  6. 2016030207 - sql50题练习(脚本)

    我的mysql版本是5.下面是要进行sql练习题使用的脚本.脚本是我整理出来的,在我本地直接复制执行就可以使用! 参考网址是:http://blog.csdn.net/zhangyulin54321/ ...

  7. Js Carousel

    http://getbootstrap.com/javascript/#carousel http://owlgraphic.com/owlcarousel/#demo https://www.mob ...

  8. 一篇文章让你彻底搞清楚Python中self的含义

    刚开始学习Python的类写法的时候觉得很是麻烦,为什么定义时需要而调用时又不需要,为什么不能内部简化从而减少我们敲击键盘的次数? 你看完这篇文章后就会明白所有的疑问. self代表类的实例,而非类. ...

  9. C语言里的指针探析——type *name[] 在函数参数里面,是一个二维指针

    type *name[] 在函数参数里面声明和不在函数里面声明其实不一样. type *name[] 如果在函数参数里声明,则name 是一个二维指针,并不是一个指针数组,而如果不在函数参数里声明,则 ...

  10. No Hibernate Session bound to thread, and configuration does not allow creat

    No Hibernate Session bound to thread, and configuration does not allow creat 今天遇到这么一个错误,在网上差了很多都没有能解 ...