BZOJ 1492: [NOI2007]货币兑换Cash 斜率优化 + splay动态维护凸包
Description
.png)
.png)
Input
Output
只有一个实数MaxProfit,表示第N天的操作结束时能够获得的最大的金钱数目。答案保留3位小数。
题解: 令 $f_{i}$ 表示第 $i$ 天所拥有的最大钱数
那么,第 $j$ 天所拥有的 $A$ 券 $x_{j}=\frac{f_{j}R_{j}}{a_{j}R_{j}+b_{j}}$, $B$ 券 $y_{j}=\frac{f_{j}}{a_{j}R_{j}+b_{j}}$
得 $f_{i}\Rightarrow x_{j}a_{i}+y_{j}b_{i}$
将方程写成一次函数的形式:$y_{j}=-\frac{a_{i}}{b_{i}}x_{j}+\frac{f_{i}}{b_{i}}$
对于 $i$来说 $a_{i},b_{i}$ 都是固定的,即斜率是一定的
将 $(x_{j},y_{j})$ 视为二维平面中的点,$f_{i}$ 最大化就是要让斜率为 $-\frac{a_{i}}{b_{i}}$ 的直线获得最大的截距
然而,斜率不是单调的,新加入点的横坐标($x_{i}$)也不是单调的,所以只能用平衡树来动态维护这个凸包.
需要支持插入一个点,查询一个直线所经过的最优点.
具体细节看代码,思路简单,代码不好写
#include<bits/stdc++.h>
#define maxn 300000
#define inf 0x3f3f3f3f
#define setIO(s) freopen(s".in","r",stdin)
using namespace std;
const double eps = 1e-9;
int root;
struct Splaytree
{
#define get(x) (ch[f[x]][1]==x)
int cnt;
int f[maxn],ch[maxn][2];
double X[maxn],Y[maxn],lk[maxn],rk[maxn];
inline void rotate(int x)
{
int old=f[x],fold=f[old],which=get(x);
ch[old][which]=ch[x][which^1],f[ch[old][which]]=old;
ch[x][which^1]=old,f[old]=x,f[x]=fold;
if(fold) ch[fold][ch[fold][1]==old]=x;
}
inline void splay(int x,int &tar)
{
int fa,u=f[tar];
for(;(fa=f[x])!=u;rotate(x))
if(f[fa]!=u)
rotate(get(fa)==get(x)?fa:x);
tar=x;
}
inline double slope(int i,int j)
{
return fabs(X[i]-X[j])<=eps ? -inf : (Y[i]-Y[j])/(X[i]-X[j]);
}
inline void insert(int &o,double x,double y,int last)
{
if(!o)
{
o=++cnt;
X[o]=x,Y[o]=y,f[o]=last;
return;
}
insert(ch[o][x-X[o]>eps],x,y,o);
}
inline int pre(int x)
{
int cur=ch[x][0],re=0;
while(cur)
{
if(slope(x,cur)+eps>=rk[cur]) re=cur,cur=ch[cur][0];
else cur=ch[cur][1];
}
return re;
}
inline int nxt(int x)
{
int cur=ch[x][1],re=0;
while(cur)
{
if(slope(x,cur)<=lk[cur]+eps) re=cur,cur=ch[cur][1];
else cur=ch[cur][0];
}
return re;
}
inline int getl(int x)
{
while(ch[x][0]) x=ch[x][0];
return x;
}
inline int getr(int x)
{
while(ch[x][1]) x=ch[x][1];
return x;
}
inline void del(int x)
{
if(!ch[x][0])
{
int right=getl(ch[x][1]);
splay(right,ch[x][1]),root=right;
ch[x][1]=f[root]=0;
lk[root]=inf;
}
else if(!ch[x][1])
{
int left=ch[x][0];
splay(left,ch[x][0]),root=left;
ch[x][0]=f[root]=0;
rk[root]=-inf;
}
else
{
int right=getl(ch[x][1]);
int left=getr(ch[x][0]);
splay(left,ch[x][0]);
splay(right,ch[x][1]);
root=left;
ch[root][1]=right,f[right]=root;
rk[root]=lk[right]=slope(root,right);
}
}
inline void maintain(int x)
{
splay(x,root);
if(ch[x][0])
{
int left=pre(x);
if(left)
{
splay(left,ch[x][0]);
ch[left][1]=f[ch[left][1]]=0;
rk[left]=lk[x]=slope(left,x);
}
else lk[x]=-inf;
}
else lk[x]=inf;
if(ch[x][1])
{
int right=nxt(x);
if(right)
{
splay(right,ch[x][1]);
ch[right][0]=f[ch[right][0]]=0;
rk[x]=lk[right]=slope(right,x);
}
else rk[x]=inf;
}
else rk[x]=-inf;
if(lk[x]-rk[x]<=eps) del(x);
}
inline int getans(int x,double k)
{
if(!x) return 0;
if(lk[x]+eps>=k&&rk[x]<=k+eps) return x;
if(lk[x]<k+eps) return getans(ch[x][0],k);
else return getans(ch[x][1],k);
}
}splay;
int n,S;
double f[maxn],A[maxn],B[maxn],rate[maxn];
int main()
{
int i,j;
// setIO("input");
scanf("%d%lf",&n,&f[0]);
for(i=1;i<=n;++i)
{
scanf("%lf%lf%lf",&A[i],&B[i],&rate[i]);
int j=splay.getans(root,-(A[i]/B[i]));
double x=splay.X[j],y=splay.Y[j];
f[i]=max(f[i-1],A[i]*x+B[i]*y);
y=f[i]/(A[i]*rate[i]+B[i]);
x=y*rate[i];
splay.insert(root,x,y,0);
splay.maintain(i);
}
printf("%.3lf",f[n]);
return 0;
}
BZOJ 1492: [NOI2007]货币兑换Cash 斜率优化 + splay动态维护凸包的更多相关文章
- [BZOJ1492] [NOI2007]货币兑换Cash 斜率优化+cdq/平衡树维护凸包
1492: [NOI2007]货币兑换Cash Time Limit: 5 Sec Memory Limit: 64 MBSubmit: 5907 Solved: 2377[Submit][Sta ...
- BZOJ.1492.[NOI2007]货币兑换(DP 斜率优化 CDQ分治/Splay)
BZOJ 洛谷 如果某天能够赚钱,那么一定会在这天把手上的金券全卖掉.同样如果某天要买,一定会把所有钱花光. 那么令\(f_i\)表示到第\(i\)天所拥有的最多钱数(此时手上没有任何金券),可以选择 ...
- BZOJ 3963: [WF2011]MachineWorks 斜率优化 + splay动态维护凸包
Description 你是任意性复杂机器公司(Arbitrarily Complex Machines, ACM)的经理,公司使用更加先进的机械设备生产先进的机器.原来的那一台生产机器已经坏了,所以 ...
- BZOJ 1492 [NOI2007]货币兑换Cash (CDQ分治/splay 维护凸包)
题目大意:太长了略 splay调了两天一直WA弃疗了 首先,我们可以猜一个贪心,如果买/卖,就一定都买/卖掉,否则不买/卖 反正货币的行情都是已知的,没有任何风险,所以肯定要选择最最最优的方案了 容易 ...
- [BZOJ1492][NOI2007]货币兑换Cash(斜率优化+CDQ分治)
1492: [NOI2007]货币兑换Cash Time Limit: 5 Sec Memory Limit: 64 MBSubmit: 5838 Solved: 2345[Submit][Sta ...
- 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分治
[BZOJ10492][NOI2007]货币兑换Cash Description 小Y最近在一家金券交易所工作.该金券交易所只发行交易两种金券:A纪念券(以下简称A券)和 B纪念券(以下简称B券).每 ...
- BZOJ 1492: [NOI2007]货币兑换Cash [CDQ分治 斜率优化DP]
传送门 题意:不想写... 扔链接就跑 好吧我回来了 首先发现每次兑换一定是全部兑换,因为你兑换说明有利可图,是为了后面的某一天两种卷的汇率差别明显而兑换 那么一定拿全利啊,一定比多天的组合好 $f[ ...
- 【BZOJ 1492】 [NOI2007]货币兑换Cash 斜率优化DP
先说一下斜率优化:这是一种经典的dp优化,是OI中利用数形结合的思想解决问题的典范,通常用于优化dp,有时候其他的一些决策优化也会用到,看待他的角度一般有两种,但均将决策看为二维坐标系上的点,并转化为 ...
随机推荐
- 20160711--C# 委托的三种调用示例(同步调用 异步调用 异步回调)【转载】
首先,通过代码定义一个委托和下面三个示例将要调用的方法: 代码如下: public delegate int AddHandler(int a,int b); public class 加法类 { p ...
- Oracle-优化SQL语句
建议不使用(*)来代替所有列名 用truncate代替delete 在SQL*Plus环境中直接使用truncate table即可:要在PL/SQL中使用,如: 创建一个存储过程,实现使用trunc ...
- source ~/.bash_profile是什么意思
~ 这个符号表示你的家目录,.bash_profile 是一个隐藏文件,主要是用来配置bash shell的,source -/.bash_profile 就是让这个配置文件在修改后立即生效.
- 【ABAP系列】SAP 业务界面同时显示KEY和文本
公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[MM系列]SAP 业务界面同时显示KEY和 ...
- C++边双缩点,Redundant Paths 分离的路径
一道比较简单的 关于边双的题,个人感觉难度不大. 求出整个图的边双,根据边双的定义我们可以延伸出 边双的任两个点都有至少两种路径来互相抵达(因为其不存在割边) .不妨将每个边双缩成一个点,样例中的图便 ...
- TensorFlow学习笔记10-卷积网络
卷积网络 卷积神经网络(Convolutional Neural Network,CNN)专门处理具有类似网格结构的数据的神经网络.如: 时间序列数据(在时间轴上有规律地采样形成的一维网格): 图像数 ...
- 20191103 《Spring5高级编程》笔记-第3章
第3章 在Spring中引入IoC和DI 依赖注入是IOC的一种特殊形式,尽管这两个术语经常可以互换使用. 3.1 控制反转和依赖注入 IOC的核心是DI,旨在提供一种更简单的机制来设置组件依赖项,并 ...
- C#里sqlDataAdapter.fill(DataSet,String)的用法
第二个参数 String是指定DataSet 里表的名字,例如 sqlDataAdapter.fill(DataSet,"学生表") 指定后,以后就可以这样调用这张表 DataSe ...
- Maven 修改jdk版本
Maven 修改jdk版本方法1: <build> <plugins> <plugin> <groupId>org.apache.maven.plugi ...
- [Python3] 019 函数:确认过参数,返回对的值
目录 0. 函数简介 1. 初识函数 2. 函数的参数与返回值 少废话,上例子 3. 查找函数的帮助文档 4. 函数的参数 (1) 参数分类 (2) 结构介绍 1) 普通参数 2) 默认参数 3) 关 ...