【问题描述】

小 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. JavaWeb用户登录功能的实现

    大四快毕业了,3年多的时间中,乱七八糟得学了一大堆,想趁找工作之前把所学的东西整理一遍,所以就尝试着做一个完整的JavaWeb系统,这几天试着做了一个用户登录的功能,分享给大家,肯定有很多不完善的地方 ...

  2. ASP.NET MVC5 easyui 之 treegrid 初用记录

    菜鸟初次使用,参考论坛中介绍的方法仍走了一些弯路,把自己遇到的问题记录下来. 1.必须定义根节点: 2.根节点一个或多个均可: 4.根节点的父节点属性不必定义,或者定义为0: 5.各级子节点的父节点属 ...

  3. [C#]『CountdownEvent』任务并行库使用小计

    System.Threading.CountdownEvent  是一个同步基元,它在收到一定次数的信号之后,将会解除对其等待线程的锁定. CountdownEvent  专门用于以下情况:您必须使用 ...

  4. Cassandra1.2文档学习(1)——Cassandra基本说明

    参考文档:http://www.datastax.com/documentation/cassandra/1.2/webhelp/index.html#cassandra/architecture/a ...

  5. javaScript & jquery完美判断图片是否加载完毕

    好久没写东西了,正好最近因为工作需要,写了一个瀑布流异步加载的程序. 今天就不谈瀑布流,来谈一下关于load的问题. ----------------------------------------- ...

  6. [转]jquery.timer用法

    转自:http://www.cnblogs.com/guohui/archive/2012/02/24/2366668.html 来自JavaEye论坛的JQuery Timers应用知识 jQuer ...

  7. 【python之路6】pycharm的使用

    1.pycharm简介 PyCharm 是我众多python编辑器中比较好的一个.而且可以跨平台,在macos和windows下面都可以用,这点比较好. PyCharm是一种Python IDE,带有 ...

  8. Java 判断操作系统类型(适用于各种操作系统)

    Java 判断操作系统类型(适用于各种操作系统) 最近一段时间写一个授权的程序,需要获取很多信息来保证程序不能随意复制使用,必须经过授权才可以. 为了限制用户使用的操作系统,必须有统一的方法来获取才可 ...

  9. 防御XSS攻击的七条原则

    本文将会着重介绍防御XSS攻击的一些原则,需要读者对于XSS有所了解,至少知道XSS漏洞的基本原理,如果您对此不是特别清楚,请参考这两篇文章:<Stored and Reflected XSS ...

  10. 【HDOJ】1983 Kaitou Kid - The Phantom Thief (2)

    不仅仅是DFS,还需要考虑可以走到终点.同时,需要进行预处理.至多封闭点数为起点和终点的非墙壁点的最小值. #include <iostream> #include <cstdio& ...