Bzoj1492: [NOI2007]货币兑换Cash(不单调的斜率优化)
题面
Sol
题目都说了
必然存在一种最优的买卖方案满足:
每次买进操作使用完所有的人民币;
每次卖出操作卖出所有的金券。
设\(f[i]\)表示第\(i\)天可以有的最大钱数
枚举\(j<i\)在第\(j\)天用完所有的钱买劵
然后在第\(i\)天卖光
获得\(60\)分
核心代码如下
for(RG int i = 1; i <= n; ++i){
f[i] = f[i - 1];
for(RG int j = 0; j < n; ++j) f[i] = max(f[i], A[j] * a[i] + B[j] * b[i]);
B[i] = f[i] / (a[i] * rate[i] + b[i]), A[i] = B[i] * rate[i];
}
优化
接着就来推柿子了
设\(X[i]=A[i]\),\(Y[i]=B[i]\),这里的\(A, B\)指的是上面代码里的\(A, B\)
考虑斜率优化
\]
假设\(k<j\)且\(X_k<X_j\)
即
\]
设\(K_i=\frac{a_i}{b_i}\)
则
\]
然后你会发现如果用单调队列来维护斜率优化的话,显然是不行的
因为\(K\)和\(X\)都是不单调的,所以显然不能单调队列
1.CDQ分治
竟然\(K\)和\(X\)不单调
那么我们让它们单调
\(CDQ\)分治每次都是考虑左边对右边的贡献
那么我们可以把左边的\(X\)递增,排个序
把右边的\(K\)递减,也排个序
那么左边用单调队列,右边就直接可以查询了
# include <bits/stdc++.h>
# define RG register
# define IL inline
# define Fill(a, b) memset(a, b, sizeof(a))
using namespace std;
const int _(1e5 + 5);
const double EPS(1e-7);
const double INF(2e9);
typedef long long ll;
int n, Q[_];
double a[_], b[_], r[_], f[_];
struct Calc{
int id;
double k, x, y;
IL void Update(){
y = f[id] / (a[id] * r[id] + b[id]), x = y * r[id];
}
} p[_], q[_];
IL int Cmp(RG Calc A, RG Calc B){
return A.k > B.k;
}
IL double Slope(RG int i, RG int j){
if(q[j].x - q[i].x < EPS) return -INF;
return (q[i].y - q[j].y) / (q[i].x - q[j].x);
}
IL void CDQ(RG int l, RG int r){
if(l == r){
f[l] = max(f[l], f[l - 1]), q[l].Update();
return;
}
RG int mid = (l + r) >> 1, be = l, en = mid + 1, head = 1, tail = 0;
for(RG int i = l; i <= r; ++i)
if(q[i].id <= mid) p[be++] = q[i];
else p[en++] = q[i];
for(RG int i = l; i <= r; ++i) q[i] = p[i];
CDQ(l, mid);
for(RG int i = l; i <= mid; ++i){
while(head < tail && Slope(Q[tail - 1], Q[tail]) < Slope(Q[tail - 1], i)) --tail;
Q[++tail] = i;
}
for(RG int i = mid + 1; i <= r; ++i){
while(head < tail && Slope(Q[head], Q[head + 1]) > q[i].k) ++head;
f[q[i].id] = max(f[q[i].id], a[q[i].id] * q[Q[head]].x + b[q[i].id] * q[Q[head]].y);
}
CDQ(mid + 1, r);
for(RG int i = l, j = mid + 1, k = l; k <= r; ++k)
if(j > r || (i <= mid && q[i].x <= q[j].x)) p[k] = q[i++];
else p[k] = q[j++];
for(RG int i = l; i <= r; ++i) q[i] = p[i];
}
int main(RG int argc, RG char* argv[]){
scanf("%d%lf", &n, &f[0]);
for(RG int i = 1; i <= n; ++i){
scanf("%lf%lf%lf", &a[i], &b[i], &r[i]);
q[i].id = i, q[i].k = -a[i] / b[i];
}
sort(q + 1, q + n + 1, Cmp);
CDQ(1, n);
printf("%.3lf\n", f[n]);
return 0;
}
2.Splay动态维护凸包
如果\(X\)单调,\(K\)不单调
那么是可以在单调队列上二分从而找到\(K\)的决策点的
然而\(X\)不单调,也就是说我们要在已有的凸包中间加入一个点
这个时候可以考虑\(Splay\)
\(Splay\)的关键字就是\(X\)
维护凸包上每个点左边直线的斜率和右边直线的斜率
过程:
- 判断如果新加的这个点在凸包内部,不管它
- 首先直接插入\(X\)并且\(Splay\)到根
- 然后在它的左子树上二分找到它能接到那个点的后面形成凸包
- 删掉那个点的右子树
- 右边同样找到可以接到它后面的点使得能形成凸包
- 删掉那个点的左子树
- 注意每次删除的时候都要维护这个点左边直线的斜率和右边直线的斜率
第一步判断的过程不好弄所以可以把它放在第\(6\)步后
显然这个点如果在凸包内部不会执行\(4, 6\)操作
查询就是在树上二分满足\(K\)要求的位置
# include <bits/stdc++.h>
# define RG register
# define IL inline
# define Fill(a, b) memset(a, b, sizeof(a))
using namespace std;
const int _(1e5 + 5);
const double EPS(1e-7);
const double INF(2e9);
typedef long long ll;
int n, fa[_], ch[2][_], rt;
double f, X[_], Y[_], lk[_], rk[_];
IL double Slope(RG int i, RG int j){
if(X[i] - X[j] < EPS) return -INF;
return (Y[i] - Y[j]) / (X[i] - X[j]);
}
IL int Son(RG int x){
return ch[1][fa[x]] == x;
}
IL void Rotate(RG int x){
RG int y = fa[x], z = fa[y], c = Son(x);
if(z) ch[Son(y)][z] = x; fa[x] = z;
ch[c][y] = ch[!c][x], fa[ch[c][y]] = y;
ch[!c][x] = y, fa[y] = x;
}
IL void Splay(RG int x, RG int ff){
for(RG int y = fa[x]; y != ff; Rotate(x), y = fa[x])
if(fa[y] != ff) Son(x) ^ Son(y) ? Rotate(x) : Rotate(y);
if(!ff) rt = x;
}
IL int Query(RG int x, RG double k){
if(!x) return 0;
if(lk[x] >= k && rk[x] <= k) return x;
if(lk[x] < k) return Query(ch[0][x], k);
return Query(ch[1][x], k);
}
IL void Insert(RG int &x, RG int ff, RG int p){
if(!x){
x = p, fa[x] = ff;
Splay(x, 0);
return;
}
if(X[p] - X[x] < EPS) Insert(ch[0][x], x, p);
else Insert(ch[1][x], x, p);
}
IL int Pre(RG int x){
RG int y = ch[0][x], ret = y;
while(y){
if(lk[y] > Slope(x, y)) ret = y, y = ch[1][y];
else y = ch[0][y];
}
Splay(ret, x);
return ret;
}
IL int Suf(RG int x){
RG int y = ch[1][x], ret = y;
while(y){
if(rk[y] < Slope(y, x)) ret = y, y = ch[0][y];
else y = ch[1][y];
}
Splay(ret, x);
return ret;
}
IL void Update(RG int x){
if(ch[0][x]){
RG int p = Pre(x); fa[ch[1][p]] = ch[1][p] = 0;
rk[p] = lk[x] = Slope(x, p);
}
else lk[x] = INF;
if(ch[1][x]){
RG int p = Suf(x); fa[ch[0][p]] = ch[0][p] = 0;
lk[p] = rk[x] = Slope(p, x);
}
else rk[x] = -INF;
if(lk[x] <= rk[x]){
fa[rt = ch[0][x]] = 0, fa[ch[1][rt] = ch[1][x]] = rt;
rk[rt] = lk[ch[1][x]] = Slope(ch[1][x], rt);
}
}
int main(RG int argc, RG char* argv[]){
scanf("%d%lf", &n, &f);
for(RG int i = 1; i <= n; ++i){
RG double a, b, r;
scanf("%lf%lf%lf", &a, &b, &r);
RG int j = Query(rt, -a / b);
f = max(f, X[j] * a + Y[j] * b);
Y[i] = f / (a * r + b), X[i] = Y[i] * r;
Insert(rt, 0, i), Update(rt);
}
printf("%.3lf\n", f);
return 0;
}
Bzoj1492: [NOI2007]货币兑换Cash(不单调的斜率优化)的更多相关文章
- BZOJ1492: [NOI2007]货币兑换Cash(CDQ分治,斜率优化动态规划)
Description 小Y最近在一家金券交易所工作.该金券交易所只发行交易两种金券:A纪念券(以下简称A券)和 B纪念券(以下 简称B券).每个持有金券的顾客都有一个自己的帐户.金券的数目可以是一个 ...
- [BZOJ1492] [NOI2007] 货币兑换Cash(cdq分治+斜率优化)
[BZOJ1492] [NOI2007] 货币兑换Cash(cdq分治+斜率优化) 题面 分析 dp方程推导 显然,必然存在一种最优的买卖方案满足:每次买进操作使用完所有的人民币:每次卖出操作卖出所有 ...
- [BZOJ1492][NOI2007]货币兑换Cash(斜率优化+CDQ分治)
1492: [NOI2007]货币兑换Cash Time Limit: 5 Sec Memory Limit: 64 MBSubmit: 5838 Solved: 2345[Submit][Sta ...
- bzoj1492[NOI2007]货币兑换Cash cdq分治+斜率优化dp
1492: [NOI2007]货币兑换Cash Time Limit: 5 Sec Memory Limit: 64 MBSubmit: 5541 Solved: 2228[Submit][Sta ...
- [BZOJ1492] [NOI2007]货币兑换Cash 斜率优化+cdq/平衡树维护凸包
1492: [NOI2007]货币兑换Cash Time Limit: 5 Sec Memory Limit: 64 MBSubmit: 5907 Solved: 2377[Submit][Sta ...
- bzoj千题计划237:bzoj1492: [NOI2007]货币兑换Cash
http://www.lydsy.com/JudgeOnline/problem.php?id=1492 dp[i] 表示 第i天卖完的最大收益 朴素的dp: 枚举从哪一天买来的在第i天卖掉,或者是不 ...
- BZOJ1492: [NOI2007]货币兑换Cash 【dp + CDQ分治】
1492: [NOI2007]货币兑换Cash Time Limit: 5 Sec Memory Limit: 64 MB Submit: 5391 Solved: 2181 [Submit][S ...
- cdq分治入门--BZOJ1492: [NOI2007]货币兑换Cash
n<=100000天,一开始有s块钱,每天股票A价格ai,B价格bi,每天可以做的事情:卖出股票:按A:B=RTi的比例买入股票.问最后的最大收益.股票可以为浮点数,答案保留三位. 用脚指头想想 ...
- [NOI2007]货币兑换 「CDQ分治实现斜率优化」
首先每次买卖一定是在某天 $k$ 以当时的最大收入买入,再到第 $i$ 天卖出,那么易得方程: $$f_i = \max \{\frac{A_iRate_kf_k}{A_kRate_k + B_k} ...
随机推荐
- iOS hook原理
OC中的method其实是一个结构体 struct objc_method{ SEL method_name char *method_types IMP method_imp } SEL是方法名,I ...
- proxy思考
vue从2.5版本之后开始强化typescript的支持程度,在ide方面增强对visual stadio的支持,更重要的是,双数据绑定那一块,要使用es6的proxy来代替Object.define ...
- PHP curl 上传文件版本兼容问题
[摘要:做微疑开辟挪用微疑接心上传文件时,总是返回 {"errcode":41005,"errmsg":"media data missing hin ...
- 来自Ch大学时期的第一份随笔!
首先,还是挺开心的!因为在大学时间总算可以脱离一点高中时期的感觉,不用再上交纸质作业(这也是我为什么要选计算机专业的原因).其次就是,大学生活总算开始有一些不一样了!学习了近两年程序语言的我们,总算可 ...
- jquery实现简易的计算器
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- mysqldump 导出提示Couldn't execute SELECT COLUMN_NAME...
mysqldump命令: 导出数据库:mysqldump -h ip -u root -p dbname > db.sql; 导出数据库中的某个表:mysqldump -h ip -u root ...
- BT网站-IBMID.COM
最近把網站改版了,主要是更改了搜索引擎. 大家可以訪問 什么是磁力链接(IBMID.COM)(Magnet URI)? 简单的说:类似下面这样以“magnet:?”开头的字符串,就是一条“磁力链接” ...
- Git服务器搭建笔记
前言:最近公司要使用git服务器对Android4.4的源码进行版本控制,所以花了些时间在Ubuntu14.04上搭建了git服务器,正好前段时间也学习了下git的使用哈哈 ------------- ...
- Tomcat中查看JVM内存使用情况
TOMCAT运行时,实时监控当前应用JVM的使用情况:可以利用Tomcat自带的应用manager查看详情. 首先,确认服务目录webapps下有manager应用 其次,需要创建角色manager和 ...
- Spring boot 项目部署服务器
Spring Boot 有两种部署到服务器的方式,这里介绍官方推荐的(jar包) 一.首先进行application.properties配置 # EMBEDDED SERVER CONFIGURAT ...