先说一下斜率优化:这是一种经典的dp优化,是OI中利用数形结合的思想解决问题的典范,通常用于优化dp,有时候其他的一些决策优化也会用到,看待他的角度一般有两种,但均将决策看为二维坐标系上的点,并转化为维护凸壳,一种根据两点的斜率与某一常数的大小关系推断二者的优劣,一种将转移方程化为相关直线方程,通过取得最大(小)截距来求最优解。关于其实现方法上,当点的x坐标单调时,可依据比较常数是否单调选择单调队列或单调栈,而当其x坐标不单调时常常使用CDQ分治或平衡树来实现。

  千万别用替罪羊来写动态凸壳!!!

  用平衡树来写动态凸壳的话,很容易想到的是维护凸壳点集并使x坐标单调,那么这个时候你不仅得到了单调的x坐标,点与点之间的斜率也就是单调的了,这个时候你就可以给每个点再维护两个值:他与他在凸壳上左边的点连成的线段的斜率和他与他在凸壳上右边的点连成的线段的斜率(边界设为正无穷和负无穷),维护了这两个值你就可以直接在二叉树上查找最优决策点,而不用二分。当插入一个点的时候,你可以在这个点两边暴力pop,这样均摊nlogn,也可以直接在这个点两边进行二叉查找这样严格nlogn,但是常数相对较小。用splay或者Treap(无旋有旋都可以,只是常数差异)会很优秀,但是用替罪羊的话,呵呵.......不仅码量爆炸,而且各种操作都不是很好实现,就拿维护上面说的两个值来讲,如果你的替罪羊删除用的是标记,那么你就要**了.....因为废点在二叉查找的时候也需要有指示作用,但是你不能把他放入凸壳来维护,所以你还要维护一下盖住他的线段的斜率......(可能写替罪羊的时候把维护的信息统一改为前驱实点和后继实点会好很多)

  下面是巨丑巨慢的替罪羊程序.....

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define ft first
#define sd second
#define mmp(a,b) (std::make_pair(a,b))
#define get_k(x,y) (((x)->b-(y)->b)/((x)->a-(y)->a))
typedef double db;
typedef std::pair<db,db> pdd;
const int N=;
const db eps=1e-;
const db Inf=./.;
const db oo=-./.;
db A[N],B[N],R[N],f[N];
int n,s;
namespace SGT{
const db alpha=0.75;
struct ScapeGoat_Tree{
ScapeGoat_Tree *ch[],*zz;
int size,cover,ex;
db a,b,lk,rk;
inline void pushup(){
size=ch[]->size+ch[]->size+ex;
cover=ch[]->cover+ch[]->cover+;
}
inline void update();
inline bool isbad(){
return cover*alpha+<ch[]->cover||cover*alpha+<ch[]->cover;
}
}*root,*null,node[N],*list[N];
int len,sz;
inline void ScapeGoat_Tree:: update(){
if(ch[]->zz!=null)return void(zz=ch[]->zz);
if(ex)return void(zz=this);
zz=ch[]->zz;
}
inline void Init(){
null=node+(sz++);
null->ch[]=null->ch[]=null->zz=null;
root=null;
}
inline void travel(ScapeGoat_Tree *p){
if(p==null)return;
travel(p->ch[]);
if(p->ex)list[++len]=p;
travel(p->ch[]);
}
inline ScapeGoat_Tree *divide(int l,int r){
if(l>r)return null;
int mid=(l+r)>>;
list[mid]->ch[]=divide(l,mid-);
list[mid]->ch[]=divide(mid+,r);
list[mid]->pushup();
list[mid]->update();
return list[mid];
}
inline void rebuild(ScapeGoat_Tree *&p){
len=,travel(p),p=divide(,len);
}
inline int get_rank(db x){
int ret=;
ScapeGoat_Tree *p=root;
while(p!=null)
if(p->a<x+eps)
ret+=p->ch[]->size+p->ex,p=p->ch[];
else
p=p->ch[];
return ret;
}
inline ScapeGoat_Tree *get_kth(int k){
ScapeGoat_Tree *p=root;
while(true)
if(p->ex&&p->ch[]->size+p->ex==k)
return p;
else if(p->ch[]->size>=k)
p=p->ch[];
else k-=p->ch[]->size+p->ex,p=p->ch[];
}
inline ScapeGoat_Tree **insert(ScapeGoat_Tree *&p,db x,db y){
if(p==null){
p=node+(sz++);
p->ch[]=p->ch[]=null;
p->size=p->cover=p->ex=;
p->a=x,p->b=y,p->zz=p;
return &null;
}
++p->size,++p->cover;
ScapeGoat_Tree **ret=insert(p->ch[p->a<x],x,y);
if(p->isbad())ret=&p;
p->update();
return ret;
}
inline void Insert(db x,db y){
int k=get_rank(x);
ScapeGoat_Tree *p1,*p2,*p3;
ScapeGoat_Tree **p=insert(root,x,y);
p2=node+(sz-);
if(k!=){
p1=get_kth(k);
p1->rk=p2->lk=get_k(p1,p2);
}else
p2->lk=Inf;
if(k+<=root->size){
p3=get_kth(k+);
p2->rk=p3->lk=get_k(p2,p3);
}else
p2->rk=oo;
if(*p!=null)rebuild(*p);
}
inline void del(ScapeGoat_Tree *p,int k){
--p->size;
if(p->ex&&p->ch[]->size+p->ex==k){
p->ex=,p->update();
return;
}
if(p->ch[]->size>=k)del(p->ch[],k);
else del(p->ch[],k-p->ch[]->size-p->ex);
p->update();
}
inline void Del(ScapeGoat_Tree *p){
int k=get_rank(p->a);
del(root,k);
ScapeGoat_Tree *p1,*p2;
if(root->size!=){
if(k==){
p2=get_kth(k);
p2->lk=Inf;
}else if(k>root->size){
p1=get_kth(k-);
p1->rk=oo;
}else{
p2=get_kth(k);
p1=get_kth(k-);
p1->rk=p2->lk=get_k(p1,p2);
}
}
if(root->size+<root->cover*alpha)rebuild(root);
}
inline void Del(int k){
del(root,k);
ScapeGoat_Tree *p1,*p2;
if(root->size!=){
if(k==){
p2=get_kth(k);
p2->lk=Inf;
}else if(k>root->size){
p1=get_kth(k-);
p1->rk=oo;
}else{
p2=get_kth(k);
p1=get_kth(k-);
p1->rk=p2->lk=get_k(p1,p2);
}
}
if(root->size+<root->cover*alpha)rebuild(root);
}
inline bool die(int k){
ScapeGoat_Tree *p=get_kth(k);
return p->lk<p->rk+eps;
}
inline void Ins(db x,db y){
int k=get_rank(x);
if(k==){
Insert(x,y),++k;
}else{
ScapeGoat_Tree *p=get_kth(k);
if(fabs(p->a-x)<eps)
p->b=std::max(p->b,y);
else
Insert(x,y),++k;
}
if(die(k))return void(Del(k));
while(k!=root->size&&die(k+))Del(k+);
while(k!=&&die(k-))Del(k-),--k;
}
inline pdd query(ScapeGoat_Tree *p,db k){
if(p->ex&&k<=p->lk+eps&&eps+k>=p->rk)
return mmp(p->a,p->b);
if((p->ex&&k>p->lk+eps)||(p->ex==&&(p->ch[]->size==||p->ch[]->zz->lk<k)))
return query(p->ch[],k);
else
return query(p->ch[],k);
}
}
int main(){
scanf("%d%d",&n,&s);
int i;SGT::Init();
for(i=;i<=n;++i)
scanf("%lf%lf%lf",&A[i],&B[i],&R[i]);
f[]=s;
db y=s/(R[]*A[]+B[]),x=y*R[];
SGT::Insert(x,y);
pdd ret;
for(i=;i<=n;++i){
ret=SGT::query(SGT::root,-A[i]/B[i]);
f[i]=B[i]*ret.sd+A[i]*ret.ft;
f[i]=std::max(f[i],f[i-]);
y=f[i]/(R[i]*A[i]+B[i]),x=y*R[i];
SGT::Ins(x,y);
}
printf("%.3f",f[n]);
return ;
}

【BZOJ 1492】 [NOI2007]货币兑换Cash 斜率优化DP的更多相关文章

  1. BZOJ 1492: [NOI2007]货币兑换Cash 斜率优化 + splay动态维护凸包

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

  2. 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的数量. 然后就很明显了...平衡 ...

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

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

  4. [BZOJ1492] [NOI2007]货币兑换Cash 斜率优化+cdq/平衡树维护凸包

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

  5. 【BZOJ1492】[NOI2007]货币兑换Cash 斜率优化+cdq分治

    [BZOJ10492][NOI2007]货币兑换Cash Description 小Y最近在一家金券交易所工作.该金券交易所只发行交易两种金券:A纪念券(以下简称A券)和 B纪念券(以下简称B券).每 ...

  6. ●BZOJ 1492 [NOI2007]货币兑换Cash

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

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

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

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

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

  9. P4027 [NOI2007]货币兑换(斜率优化dp+cdq分治)

    P4027 [NOI2007]货币兑换 显然,如果某一天要买券,一定是把钱全部花掉.否则不是最优(攒着干啥) 我们设$f[j]$为第$j$天时用户手上最多有多少钱 设$w$为花完钱买到的$B$券数 $ ...

随机推荐

  1. git push之后回滚(撤销)代码

    问题描述:首先,先说明一下,为什么会引发这次的话题,是这样的,我做完功能Agit push之后,2个月后需求部门要求不要功能A了,然后需要在没有功能A的基础上开发,怎么办?赶紧回滚代码呀. 然后我用g ...

  2. Ubuntu设置代理服务器

    由于公司网络的原因,apache的网站访问不了,对于需要经常访问apache网站查看文档的我,最近想了一种方法,在自己的阿里云服务器上搭建一个代理服务器.经过查资料,最终决定使用TinyProxy. ...

  3. jmeter 插件安装

    1.下载Plugins Manager插件 打开下载插件地址:https://jmeter-plugins.org/ 2.将下载的plugins-manager.jar包复制到Jmeter安装目录,l ...

  4. YUM工具使用

    一.yum命令概述: 1.简介: yum命令时在Fedora和RedHat以及SUSE中基于rpm的软件包管理器,它可以使系统管理人员交互和自动化地更细与管理RPM软件包,能够从指定的服务器自动下载R ...

  5. CAS解扰小结

    1.每个ts数据包由:1.包头 2.包数据 包头有个字段 PID ,该字段指示包数据的类型.比如说: PID 为 0x0000 包数据的类型就是 PAT表 PID 为 0x0001 包数据的类型就是 ...

  6. 三角形xjoi 8.14

    问题描述:离圣诞节只有一个月了,家里要你准备一个很大的星星,然后把它粘在圣诞树的顶端.你已经准备好了一个三角形的银色包装纸来做星星,可忽然有一天你发现在这张大纸上被弄了好多的小洞,原来是你的弟弟妹妹已 ...

  7. 一步一步学Linq to sql(三):增删改

    示例数据库 字段名 字段类型 允许空 字段说明 ID uniqueidentifier 表主键字段 UserName varchar(50) 留言用户名 PostTime datetime 留言时间 ...

  8. How to enable download EXE files from the Sharepoint website

          As we all know,many applications have forbidden to upload and download exe files.Because the e ...

  9. 云计算之路-阿里云上:奇怪的CPU 100%问题

    这篇博文记录一下6月1日在阿里云上遇到的奇怪的CPU 100%问题,希望多年以后能真相大白. 那天负载均衡(SLB)中只放了1台云服务器(平时都放2台),由于是节假日,虽然只放了一台,但这台服务器的负 ...

  10. CS229 1

    1.机器学习 机器学习是工具,具体应用到某个实际场景下,才是目的. 2.分类 a 监督学习,包括回归(regression),分类(classification).回归问题,数据可以是连续或者离散,分 ...