先说一下斜率优化:这是一种经典的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. zabbix监控nginx服务状态

    nginx需要安装--with-http_stub_status_module模块 $ nginx -V nginx version: nginx/1.12.2 built by gcc 4.8.5 ...

  2. 【Markdown】Markdown的使用(自用)

    # 欢迎使用 Cmd Markdown 编辑阅读器 我们理解您需要更便捷更高效的工具记录思想,整理笔记.知识,并将其中承载的价值传播给他人,Cmd Markdown 是我们给出的答案 -- 我们为记录 ...

  3. (数据科学学习手札24)逻辑回归分类器原理详解&Python与R实现

    一.简介 逻辑回归(Logistic Regression),与它的名字恰恰相反,它是一个分类器而非回归方法,在一些文献里它也被称为logit回归.最大熵分类器(MaxEnt).对数线性分类器等:我们 ...

  4. SPOJ SUBLEX

    SUBLEX - Lexicographical Substring Search 链接 题意 求第k小的子串.相同的算一个. 分析 建立后缀自动机,在后缀自动机上从一个点经过trans,到另一个点, ...

  5. 1977: [BeiJing2010组队]次小生成树 Tree

    1977: [BeiJing2010组队]次小生成树 Tree https://lydsy.com/JudgeOnline/problem.php?id=1977 题意: 求严格次小生成树,即边权和不 ...

  6. C++各种类型的简单排序大汇总~

    啊,排序的技能点也太多了吧!!!LITTLESUN快要**在排序的技能场了啊!(划掉)经历了两天48小时2880分钟172800秒的艰苦奋斗,终于终于终于学的差不多了!明天就可以去打排序的小怪喽!(撒 ...

  7. cadence17.2的OrCAD启动找不到license的问题

    1. cadence17.2的OrCAD每次启动都说找不到license 2. 提示是找不到licence,看下系统变量,licence的路径是在的 3. 估计是读取licence的路径的服务未开启, ...

  8. ExtJs4.1目录结构介绍和使用说明[转]

    一.在做ExtJs开发之前首先要到网站上下载ExtJs的开发包,我用的最新版本是4.1.1.此版本相对于之前的版本目录结构发生了一些变化,没有了adapter目录, 目录结构如下 文件/文件夹名的作用 ...

  9. Android Studio Gradle编译时『No resource found that matches the given name』解决方法(windows系统的坑)

    * 最近帮团队同事配置gradle时,发现一个非常奇怪的问题:> * 同样的gradle配置的项目,只是修改了一个项目的名称,竟然会出现以下奇怪问题: ## 现象1. 一个编译完全OK,另外一个 ...

  10. SDOI2013森林

    题面 主席树启发式合并,每次连边维护并查集,集合大小,求lca所需信息,合并两个树上的主席树, 重点看代码. #include <iostream> #include <algori ...