NOI2007 货币兑换
【问题描述】
小 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 货币兑换的更多相关文章
- 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的数量. 然后就很明显了...平衡 ...
- bzoj1492[NOI2007]货币兑换Cash cdq分治+斜率优化dp
1492: [NOI2007]货币兑换Cash Time Limit: 5 Sec Memory Limit: 64 MBSubmit: 5541 Solved: 2228[Submit][Sta ...
- 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; ...
- P4027 [NOI2007]货币兑换(斜率优化dp+cdq分治)
P4027 [NOI2007]货币兑换 显然,如果某一天要买券,一定是把钱全部花掉.否则不是最优(攒着干啥) 我们设$f[j]$为第$j$天时用户手上最多有多少钱 设$w$为花完钱买到的$B$券数 $ ...
- bzoj千题计划237:bzoj1492: [NOI2007]货币兑换Cash
http://www.lydsy.com/JudgeOnline/problem.php?id=1492 dp[i] 表示 第i天卖完的最大收益 朴素的dp: 枚举从哪一天买来的在第i天卖掉,或者是不 ...
- [NOI2007]货币兑换 cdq分治,斜率优化
[NOI2007]货币兑换 LG传送门 妥妥的\(n \log n\)cdq做法. 这题用cdq分治也可以\(n \log n\)但是在洛谷上竟然比一些优秀的splay跑得慢真是见了鬼了看来还是人丑常 ...
- 洛谷 P4027 [NOI2007]货币兑换 解题报告
P4027 [NOI2007]货币兑换 题目描述 小 \(Y\) 最近在一家金券交易所工作.该金券交易所只发行交易两种金券:\(A\) 纪念券(以下简称 \(A\) 券)和 \(B\) 纪念券(以下简 ...
- [NOI2007]货币兑换 --- DP + 斜率优化(CDQ分治)
[NOI2007]货币兑换 题目描述: 小 Y 最近在一家金券交易所工作.该金券交易所只发行交易两种金券:A 纪念券(以下简称 A 券)和 B 纪念券(以下简称 B 券). 每个持有金券的顾客都有一个 ...
- [BZOJ1492][NOI2007]货币兑换Cash(斜率优化+CDQ分治)
1492: [NOI2007]货币兑换Cash Time Limit: 5 Sec Memory Limit: 64 MBSubmit: 5838 Solved: 2345[Submit][Sta ...
- BZOJ1492: [NOI2007]货币兑换Cash 【dp + CDQ分治】
1492: [NOI2007]货币兑换Cash Time Limit: 5 Sec Memory Limit: 64 MB Submit: 5391 Solved: 2181 [Submit][S ...
随机推荐
- apache虚拟主机安装注意事项
apache虚拟主机在添加的时候,总是会有一些莫名其妙的问题,后来发现可以使用一个参数去验证的: xxxxx/httpd -S //这个参数会去检查虚拟主机配置的正确性,很好用 因为有时候可能就是缩进 ...
- JS当前日期相加相减
function DateAddORSub(interval,type,number) { /* * 功能:实现Script的Date加减功能. * 参数:interval,字符串表达式,表示要添加的 ...
- 如何实现.so共享库文件
.so共享库相当于window中的.DLL文件 两个进程同时调用了.so文件,进程就会加载的.so文件到各自的内存空间,而不能实现进程间通讯. .so文件编译的方法: -so文件不需要main文件,即 ...
- 初用jquery
---恢复内容开始--- 这两天在顶顶大人的指导下,利用jquery框架做了一个动态切换的小页面.最终效果图: 这么萌萌哒的图片让我觉得一直在测试也没那么累.实现功能如下: 1.打开页面时,自动切换, ...
- [C#]异步委托使用小计
APM(=Asynchronous Programming Model(=异步编程模型)) 使用 IAsyncResult 设计模式的异步操作是通过名为 Begin操作名称 和 End操作名称 的两个 ...
- MySQL修改时区的方法小结
这篇文章主要介绍了MySQL修改时区的方法,总结分析了三种常见的MySQL时区修改技巧,包括命令行模式.配置文件方式及代码方式,需要的朋友可以参考下 方法一:通过mysql命令行模式下动态修改 1.1 ...
- sitecustomize.py 用法
1.在python安装目录下的lib下的site-packages 目录中,新建文件sitecustomize.py.这是个特殊的文件,在python启动时会自动执行其中的语句.在sitecustom ...
- 如何在Win10中启用和关闭管理员账户?
和Win7/Win8.1一样,Win10的管理员账户Administrator是默认隐藏和关闭的,因为该账户权限极高,被不法分子利用后存在极大风险.但如果你想在某些特殊情况下使用该账户,就需要手动将其 ...
- 【转载】ASP.NET获取路径的方法
HttpContext.Current.Request.PhysicalPath; // 获得当前页面的完整物理路径.比如 F:\XFU.NSQS\project\website\Default ...
- 一周一话题之四(JavaScript、Dom、jQuery全面复习总结<jQuery篇>)
-->目录导航 一. 初探Jquery 1. 介绍 2. 基础 二. Jquery操作 1. jQuery页面加载 2. 选择器 3. 操作Dom 三. Jquery进阶 1. 隐式迭代与链式编 ...