Description

最初你有 S 块钱, 有 N 天给你来兑换货币, 求最大获利.

一共只有两种货币 A , B .

对于每一天, 给定 3 个系数 A[i], B[i], Rate[i]

A[i] 表示当天 A 货币的单位价值, B[i] 表示当前 B 货币的单位价值.

第\(i\)天你可以进行以下两种操作: (可以执行多次)

  ① 将 OP% 的 A 货币和 OP% 的 B 货币卖出.

  ② 按照 A : B = Rate[i] 的比例, 用一部分的钱买入货币.

\(n \le 100000\)

Analysis

考虑假如有一天买货币没有用完所有钱更优, 说明这里的前用了后面赚不回来

同理可知: 要么一次性买入货币买光所有钱, 要么一次卖光所有货币

我们将相邻的买卖分为一组

记\(f[i]\)为第\(i\)天(在买之前)最多能有多少钱

于是\(f[i] = \max\{f[i-1], D[j] * A[i] + C[j] * B[i]\}\)

其中\(C[i] = \frac{f[i]}{A[i]*Rate[i] + B[i]}\) 即花光钱能买多少个\(B\)货币

\(D[i] = C[i] * Rate[i]\)即花光钱能买多少个\(A\)货币

这是一个可以斜率优化的式子.

但是注意到\(A,B,C,D\)什么的都并没有单调性

Solution 1

动态维护凸壳

Solution 2

注意到转移只与前面求出来的量有关

那么我们可以进行cdq分治

这样我们就可以排序建凸包, 排序切线询问.

Notice

不是一般序列上的斜率优化, 最好使用\(\det\)

对于切线询问, 除了可以用斜率判, 也可以直接根据答案的单峰性判断, 具体见代码

另外, 凸包比较还是用\(\le ,\ge\)这两个符号好一些

Code

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <cmath>
#include <algorithm>
#include <iostream>
#define rep(i,a,b) for (int i = (a); i <= (b); ++ i)
#define per(i,a,b) for (int i = (a); i >= (b); -- i)
#define For(i,a,b) for (int i = (a); i < (b); ++ i)
using namespace std;
const int M = 1e5 + 7;
typedef double db; inline int ri(){
int x = 0; bool f = 1; char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = 0;
for (; isdigit(c); c = getchar()) x = x*10+c-48;
return f ? x : -x;
} int n;
db f[M];
db A[M], B[M], C[M], D[M], R[M]; inline db det(db x, db y, db vx, db vy) {return x * vy - y * vx;}
inline bool side(int x, int y, int z) {return det(D[y]-D[x], C[y]-C[x], D[z]-D[x], C[z]-C[x]) >= 0;}
inline db calc(int x, int y) {return D[x] * A[y] + C[x] * B[y];}
inline db V(int x) {return - A[x] / B[x];}
inline bool cmp1(int x, int y) {return D[x] < D[y];}
inline bool cmp2(int x, int y) {return V(x) > V(y);} void cdq(int l, int r){
static int q[M], v[M]; if (l == r) {
f[l] = max(f[l], f[l-1]);
C[l] = f[l] / (A[l] * R[l] + B[l]);
D[l] = C[l] * R[l];
return;
} int mid = l+r >> 1; cdq(l, mid); rep (i, l, mid) q[i] = i;
sort(q+l, q+mid+1, cmp1);
int h = l, t = l-1;
rep (i, l, mid){
while (h < t && side(q[t-1], q[t], q[i]) ) --t;
q[++t] = q[i];
} rep (i, mid+1, r) v[i] = i;
sort(v+mid+1, v+r+1, cmp2);
rep (i, mid+1, r){
int y = v[i];
while (h < t && calc(q[h], y) <= calc(q[h+1], y)) ++h;
f[y] = max(f[y], calc(q[h], y));
} cdq(mid+1, r);
} int main(){
#ifndef ONLINE_JUDGE
freopen("a.in", "r", stdin);
#endif scanf("%d%lf", &n, &f[0]); rep (i, 1, n) scanf("%lf%lf%lf", &A[i], &B[i], &R[i]); cdq(1, n); printf("%.3lf\n", f[n]); return 0;
}

BZOJ 1492 [NOI2007] - cash的更多相关文章

  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. ●BZOJ 1492 [NOI2007]货币兑换Cash

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

  3. bzoj 1492 [NOI2007]货币兑换Cash(斜率dp+cdq分治)

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=1492   [题意] 有AB两种货币,每天可以可以付IPi元,买到A券和B券,且A:B= ...

  4. 斜率优化(CDQ分治,Splay平衡树):BZOJ 1492: [NOI2007]货币兑换Cash

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

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

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

  6. bzoj 1492: [NOI2007]货币兑换Cash

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

  7. BZOJ 1492 [NOI2007]货币兑换Cash:斜率优化dp + cdq分治

    传送门 题意 初始时你有 $ s $ 元,接下来有 $ n $ 天. 在第 $ i $ 天,A券的价值为 $ A[i] $ ,B券的价值为 $ B[i] $ . 在第 $ i $ 天,你可以进行两种操 ...

  8. bzoj 1492: [NOI2007]货币兑换Cash【贪心+斜率优化dp+cdq】

    参考:http://www.cnblogs.com/lidaxin/p/5240220.html 虽然splay会方便很多,但是懒得写,于是写了cdq 首先要想到贪心的思路,因为如果在某天买入是能得到 ...

  9. BZOJ 1492 [NOI2007]货币兑换Cash (CDQ分治/splay 维护凸包)

    题目大意:太长了略 splay调了两天一直WA弃疗了 首先,我们可以猜一个贪心,如果买/卖,就一定都买/卖掉,否则不买/卖 反正货币的行情都是已知的,没有任何风险,所以肯定要选择最最最优的方案了 容易 ...

随机推荐

  1. YCM的使用

    YCM大概用来做3件事: (1)自动补全 (2)检查语法 (3)跳转到函数定义 然后涉及到两个关键的文件.vimrc 和 .ycm_extra_conf.py 首先需要正确的配置,在.vimrc中写入 ...

  2. poj 2579 中位数问题 查找第K大的值

    题意:对列数X计算∣Xi – Xj∣组成新数列的中位数. 思路:双重二分搜索 对x排序 如果某数大于 mid+xi 说明在mid后面,这些数的个数小于 n/2 的话说明这个中位数 mid 太大 反之太 ...

  3. 20145202 《Java程序设计》第四周学习总结

    继承:打破了封装性 extends 1.提高了代码的复用性. 2.让类与类之间产生了关系,有了这个关系,才有了多态的特性. 3.必须是类与类之间有所属类关系才可以继承. 4.java只支持单继承不支持 ...

  4. spring里面的context:component-scan

    原文:http://jinnianshilongnian.iteye.com/blog/1762632 component-scan的作用的自动扫描,把扫描到加了注解Java文件都注册成bean &l ...

  5. laravel5.5门面

    Facades为应用程序的 服务容器 中可用的类提供了一个 静态接口 . 最直观的好处 就是需记住必须手动注入或配置的长长的类名.因此有人也理解Facades就是一个"快捷别名" ...

  6. Android学习笔记(四)之碎片化Fragment实现仿人人客户端的侧边栏

    其实一种好的UI布局,可以使用户感到更加的亲切与方便.最近非常流行的莫过于侧边栏了,其实我也做过很多侧边栏的应用,但是那些侧边栏的使用我 都不是很满意,现在重新整理,重新写了一个相对来说我比较满意的侧 ...

  7. 使用自己的类来作为hashtable的主键

    import java.util.*; class Counter { } class Groundhog2 { int ghNumber; Groundhog2(int n) { ghNumber ...

  8. 《Cracking the Coding Interview》——第18章:难题——题目10

    2014-04-29 04:22 题目:给定一堆长度都相等的单词,和起点.终点两个单词,请从这堆单词中寻找一条变换路径,把起点词变成终点词,要求每次变换只能改一个字母. 解法:Leetcode中有Wo ...

  9. DDT驱动

    下载ddt并安装 Pip install ddt 或者官网下载安装 http://ddt.readthedocs.io/en/latest/ https://github.com/txels/ddt ...

  10. Mecanim动画

    1.基础 现在Animation编辑器给个模型设计一个动画,都会自动为此模型加上Animator组件,并产生一个controller后缀的控制器和一个相关的anim后缀的动画剪辑, unity根据An ...