首先每次买卖一定是在某天 $k$ 以当时的最大收入买入,再到第 $i$ 天卖出,那么易得方程:

$$f_i = \max \{\frac{A_iRate_kf_k}{A_kRate_k + B_k} + \frac{B_if_k}{A_kRate_k + B_k}\}$$

再令

$$\left\{\begin{aligned} x_k = \frac{Rate_kf_k}{A_kRate_k + B_k} \\ y_k = \frac{f_k}{A_kRate_k + B_k}\end{aligned}\right.$$

则有

$$\begin{aligned} f_i &= \max \{A_ix_k + B_iy_k\} \\ y_k &= - \frac{A_i}{B_i}x_k + \frac{f_i}{B_i} \end{aligned}$$

那么现在需要找到一个点 $(x_k, y_k)$ 使得直线的截距最大

由于斜率和横坐标皆不满足单调性,可以用平衡树等维护,这里使用CDQ分治实现

实现过程如下:

Ⅰ 将数据按照斜率$\frac{A_i}{B_i}$降序排序

Ⅱ 将区间按照操作顺序分为左右两部分处理

Ⅲ 先处理左半部分,维护左半边凸包(注意,此时左半边已按照 $x$ 排序)

Ⅳ 处理左半边对右半边的影响,由于已按照斜率降序排序,所以普通斜率优化即可

Ⅴ 将区间按照 $x$ 排序

代码

 #include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath> using namespace std; const int MAXN = 1e05 + ; const double INF = 1e60;
const double eps = 1e-; int Dcmp (double p) {
if (fabs (p) < eps)
return ;
return p < ? - : ;
} struct CashSt {
double a, b, rate;
double k, x, y;
int index; CashSt () {} bool operator < (const CashSt& p) const {
return Dcmp (x - p.x) == ? Dcmp (y - p.y) < : Dcmp (x - p.x) < ;
}
} ;
CashSt Cash[MAXN];
bool comp (const CashSt& a, const CashSt& b) {
return Dcmp (a.k - b.k) > ;
} int N; double slope (CashSt a, CashSt b) {
if (Dcmp (b.x - a.x) == )
return INF;
return (b.y - a.y) / (b.x - a.x);
}
double f[MAXN]= {};
CashSt Que[MAXN];
int l = , r = ;
CashSt temp[MAXN];
void CDQ (int left, int right) {
if (left == right) {
f[left] = max (f[left], f[left - ]);
Cash[left].y = f[left] / (Cash[left].a * Cash[left].rate + Cash[left].b);
Cash[left].x = Cash[left].y * Cash[left].rate;
return ;
}
int mid = (left + right) >> ;
int p1 = left - , p2 = mid;
for (int i = left; i <= right; i ++)
Cash[i].index <= mid ? temp[++ p1] = Cash[i] : temp[++ p2] = Cash[i];
for (int i = left; i <= right; i ++)
Cash[i] = temp[i];
CDQ (left, mid);
l = , r = ;
for (int i = left; i <= mid; i ++) {
while (l < r && Dcmp (slope (Que[r - ], Que[r]) - slope (Que[r], Cash[i])) < )
r --;
Que[++ r] = Cash[i];
}
for (int i = mid + ; i <= right; i ++) {
while (l < r && Dcmp (slope (Que[l], Que[l + ]) - Cash[i].k) > )
l ++;
f[Cash[i].index] = max (f[Cash[i].index], Cash[i].a * Que[l].x + Cash[i].b * Que[l].y);
}
CDQ (mid + , right);
l = left, r = mid + ;
int p = ;
while (l <= mid && r <= right) {
// if (Dcmp (Cash[l].x - Cash[r].x) < 0 || (Dcmp (Cash[l].x - Cash[r].x) == 0 && Dcmp (Cash[l].y - Cash[r].y) < 0))
if (Cash[l] < Cash[r])
temp[++ p] = Cash[l], l ++;
else
temp[++ p] = Cash[r], r ++;
}
while (l <= mid)
temp[++ p] = Cash[l], l ++;
while (r <= right)
temp[++ p] = Cash[r], r ++;
for (int i = ; i <= p; i ++)
Cash[i + left - ] = temp[i];
} double getnum () {
double num = 0.0;
char ch = getchar ();
double T = 1.0; while (! isdigit (ch))
ch = getchar ();
while (isdigit (ch))
num = num * 10.0 + (ch - '') * 1.0, ch = getchar ();
if (ch == '.') {
ch = getchar ();
while (isdigit (ch))
num = num + (T /= 10.0) * (ch - ''), ch = getchar ();
} return num;
} int main () {
// freopen ("Input.txt", "r", stdin); scanf ("%d%lf", & N, & f[]);
for (int i = ; i <= N; i ++) {
double a = getnum (), b = getnum (), rate = getnum ();
Cash[i].a = a, Cash[i].b = b, Cash[i].rate = rate;
Cash[i].index = i;
Cash[i].k = - a / b;
}
sort (Cash + , Cash + N + , comp);
CDQ (, N);
printf ("%.3f\n", f[N]); return ;
} /*
3 100
1 1 1
1 2 2
2 2 3
*/

[NOI2007]货币兑换 「CDQ分治实现斜率优化」的更多相关文章

  1. LOJ 2353 & 洛谷 P4027 [NOI2007]货币兑换(CDQ 分治维护斜率优化)

    题目传送门 纪念一下第一道(?)自己 yy 出来的 NOI 题. 考虑 dp,\(dp[i]\) 表示到第 \(i\) 天最多有多少钱. 那么有 \(dp[i]=\max\{\max\limits_{ ...

  2. BZOJ1492: [NOI2007]货币兑换Cash(CDQ分治,斜率优化动态规划)

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

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

    [BZOJ1492] [NOI2007] 货币兑换Cash(cdq分治+斜率优化) 题面 分析 dp方程推导 显然,必然存在一种最优的买卖方案满足:每次买进操作使用完所有的人民币:每次卖出操作卖出所有 ...

  4. Codeforces Gym 101175F - Machine Works(CDQ 分治维护斜率优化)

    题面传送门 首先很明显我们会按照 \(d_i\) 的顺序从小到大买这些机器,故不管三七二十一先将所有机器按 \(d_i\) 从小到大排序. 考虑 \(dp\),\(dp_i\) 表示在时刻 \(d_i ...

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

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

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

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

  7. BZOJ1492:[NOI2007]货币兑换 (CDQ分治+斜率优化DP | splay动态维护凸包)

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

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

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

  9. Bzoj1492: [NOI2007]货币兑换Cash(不单调的斜率优化)

    题面 传送门 Sol 题目都说了 必然存在一种最优的买卖方案满足: 每次买进操作使用完所有的人民币: 每次卖出操作卖出所有的金券. 设\(f[i]\)表示第\(i\)天可以有的最大钱数 枚举\(j&l ...

随机推荐

  1. ORA-01034和ORA-27101的解决方法

    问题所在: 1.要登录的数据库实例内容配置内容错误,联系负责该机子的管理员看原因

  2. 【BZOJ3293】分金币(贪心)

    [BZOJ3293]分金币(贪心) 题面 BZOJ 洛谷 题解 和上一题一样啊. #include<cstdio> #include<cmath> #include<al ...

  3. BZOJ 3143 游走 | 数学期望 高斯消元

    啊 我永远喜欢期望题 BZOJ 3143 游走 题意 有一个n个点m条边的无向联通图,每条边按1~m编号,从1号点出发,每次随机选择与当前点相连的一条边,走到这条边的另一个端点,一旦走到n号节点就停下 ...

  4. MyBatis.2剖析

    上次给大家介绍了一下properties 和 environments 的配置, 接下来就正式开始看源码了: 上次例子中,我们以 SqlSessionFactoryBuilder 去创建 SqlSes ...

  5. 解题:洛谷2093 JZPFAR

    题面 初见K-D Tree 其实这样的题(欧几里得距离第$x$近点对)不应该用K-D Tree做,因为会被构造数据卡成$O(n^2)$,随机的另说. 但是并没有找到合适的K-D Tree的题(区域统计 ...

  6. go递归打印指定目录下的所有文件及文件夹

    func treedir(fpath string){ // 获取fileinfo if finfo,err := os.Stat(fpath); err == nil { // 判断是不是目录 如果 ...

  7. striding layers 是什么意思?

    https://www.zhihu.com/question/66283266/answer/240344515 第一句 We adapted the VGG-16 network (Simonyan ...

  8. python安装pip、numpy、scipy、statsmodels、pandas、matplotlib等

    1.安装python 2.安装numpy(开源的数值计算扩展,可用来存储和处理大型矩阵,比Python自身的嵌套列表(nested list structure)结构要高效的多. 很多库都是以此库为依 ...

  9. ROI POOLING 介绍

    转自 https://blog.csdn.net/gbyy42299/article/details/80352418 Faster rcnn的整体构架: 训练的大致过程: 1.图片先缩放到MxN的尺 ...

  10. java基础-迭代器(Iterator)与增强for循环

    java基础-迭代器(Iterator)与增强for循环 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.Iterator迭代器概述 Java中提供了很多个集合,它们在存储元素时 ...