题目传送门

纪念一下第一道(?)自己 yy 出来的 NOI 题。

考虑 dp,\(dp[i]\) 表示到第 \(i\) 天最多有多少钱。

那么有 \(dp[i]=\max\{\max\limits_{j=1}^{i-1}a[i]*(dp[j]/(a[j]*r[j]+b[j])*r[j])+b[i]*dp[j]/(a[j]*r[j]+b[j]),dp[i-1]\}\)

我们稍微观察一下,里面那个式子似乎能写成斜率优化的样子:

令 \(t[j]=dp[j]/(a[j]*r[j]+b[j])\),假设有 \(j>k\) 并且从 \(j\) 转移比从 \(k\) 转移更优,那么:

\(a[i]*t[j]*r[j]+b[i]*t[j]>a[i]*t[k]*r[k]+b[i]*t[k]\)

将这个式子变个形,可以得到:

\(\frac{t[j]*r[j]-t[k]*r[k]}{-t[j]+t[k]}>\frac{b[i]}{a[i]}\)

但显然 \(-t[j]\)(横坐标) 是不单调的,\(\frac{b[i]}{a[i]}\)(斜率)也是不单调的。

解决斜率不单调的问题,这与任务安排有点类似,把所有点扔进凸包后二分斜率找到最优决策点。

解决横坐标不单调的问题,可以借鉴Cow School的方法,使用CDQ分治将前一半的点按横坐标排序后建立凸包。

具体实现时还有诸多细节需考虑,例如 CDQ 分治中递归的顺序不能搞错,必须按照递归左半边 \(\rightarrow\) 用左半边更新右半边答案 \(\rightarrow\) 递归右半边的顺序进行,否则无法从 \(dp[i]\) 更新到 \(dp[i+1]\)。还有就是特判斜率不存在的情况。

/*
Contest: -
Problem: P4027
Author: tzc_wk
Time: 2020.7.18
*/
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fz(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define foreach(it,v) for(__typeof(v.begin()) it=v.begin();it!=v.end();it++)
#define all(a) a.begin(),a.end()
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,0x3f,sizeof(a))
#define fillsmall(a) memset(a,0xcf,sizeof(a))
#define y1 y1010101010101
#define y0 y0101010101010
typedef pair<int,int> pii;
inline int read(){
int x=0,neg=1;char c=getchar();
while(!isdigit(c)){
if(c=='-') neg=-1;
c=getchar();
}
while(isdigit(c)) x=x*10+c-'0',c=getchar();
return x*neg;
}
const double EPS=1e-10;
int n=read();
struct data{
double a,b,r,dp;
} a[100005],tmp[100005];
inline double _t(int j){
return 1.0*a[j].dp/(a[j].a*a[j].r+a[j].b);
}
inline double sl(int j,int k){
if(abs((-_t(j))-(-_t(k)))<EPS) return 1e9;
return 1.0*(_t(j)*a[j].r-_t(k)*a[k].r)/((-_t(j))-(-_t(k)));
}
inline void merge(int l,int r,int mid){
int p1=l,p2=mid+1;
for(int i=l;i<=r;i++){
if(p1<=mid&&(p2>r||_t(p1)>_t(p2)))
tmp[i]=a[p1++];
else
tmp[i]=a[p2++];
}
for(int i=l;i<=r;i++){
a[i]=tmp[i];
}
}
int q[100005];
inline int bsearch(int l,int r,double slo){
if(l==r) return q[l];
int mid=(l+r)>>1;
if(sl(q[mid+1],q[mid])-slo>EPS) return bsearch(mid+1,r,slo);
else return bsearch(l,mid,slo);
}
inline void CDQ(int l,int r){
if(l==r){
a[l+1].dp=max(a[l+1].dp,a[l].dp);
return;
}
int mid=(l+r)>>1;
CDQ(l,mid);
int hd=1,tl=0;
fz(i,l,mid){
while(hd<tl&&sl(i,q[tl])-sl(q[tl],q[tl-1])>EPS) tl--;
q[++tl]=i;
}
fz(i,mid+1,r){
int j=bsearch(hd,tl,1.0*a[i].b/a[i].a);
a[i].dp=max(a[i].dp,a[i].a*(a[j].dp/(a[j].a*a[j].r+a[j].b)*a[j].r)+a[i].b*a[j].dp/(a[j].a*a[j].r+a[j].b));
a[i+1].dp=max(a[i+1].dp,a[i].dp);
}
CDQ(mid+1,r);
merge(l,r,mid);
}
signed main(){
cin>>a[1].dp;
fz(i,1,n) scanf("%lf %lf %lf",&a[i].a,&a[i].b,&a[i].r);
CDQ(1,n);
printf("%.3lf\n",a[n+1].dp);
return 0;
}
/*
dp[i]=max{a[i]*(dp[j]/(a[j]*r[j]+b[j])*r[j])+b[i]*dp[j]/(a[j]*r[j]+b[j])} Let t[j] be dp[j]/(a[j]*r[j]+b[j]) a[i]*t[j]*r[j]+b[i]*t[j] j>k a[i]*t[j]*r[j]+b[i]*t[j]>a[i]*t[k]*r[k]+b[i]*t[k]
a[i]*(t[j]*r[j]-t[k]*r[k])>b[i]*(t[k]-t[j])
(t[j]*r[j]-t[k]*r[k])
--------------------->b[i]/a[i]
(-t[j]-(-t[k]))
*/

LOJ 2353 & 洛谷 P4027 [NOI2007]货币兑换(CDQ 分治维护斜率优化)的更多相关文章

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

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

  2. 洛谷P4027 [NOI2007]货币兑换

    P4027 [NOI2007]货币兑换 算法:dp+斜率优化 题面十分冗长,题意大概是有一种金券每天价值会有变化,你可以在某些时间点买入或卖出所有的金券,问最大收益 根据题意,很容易列出朴素的状态转移 ...

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

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

  4. 洛谷 P4027 [NOI2007]货币兑换 解题报告

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

  5. 洛谷P4027 [NOI2007]货币兑换(dp 斜率优化 cdq 二分)

    题意 题目链接 Sol 解题的关键是看到题目里的提示... 设\(f[i]\)表示到第\(i\)天所持有软妹币的最大数量,显然答案为\(max_{i = 1}^n f[i]\) 转移为\(f_i = ...

  6. 洛谷P3810 陌上花开(CDQ分治)

    洛谷P3810 陌上花开 传送门 题解: CDQ分治模板题. 一维排序,二维归并,三维树状数组. 核心思想是分治,即计算左边区间对右边区间的影响. 代码如下: #include <bits/st ...

  7. 洛谷P4169 天使玩偶 CDQ分治

    还是照着CDQ的思路来. 但是有一些改动: 要求4个方向的,但是可爱的CDQ分治只能求在自己一个角落方向上的.怎么办?旋转!做4次就好了. 统计的不是和,而是——max!理由如下: 设当前点是(x,y ...

  8. [NOI2007]货币兑换 「CDQ分治实现斜率优化」

    首先每次买卖一定是在某天 $k$ 以当时的最大收入买入,再到第 $i$ 天卖出,那么易得方程: $$f_i = \max \{\frac{A_iRate_kf_k}{A_kRate_k + B_k} ...

  9. [bzoj] 3263 陌上花开 洛谷 P3810 三维偏序|| CDQ分治 && CDQ分治讲解

    原题 定义一个点比另一个点大为当且仅当这个点的三个值分别大于等于另一个点的三个值.每比一个点大就为加一等级,求每个等级的点的数量. 显然的三维偏序问题,CDQ的板子题. CDQ分治: CDQ分治是一种 ...

随机推荐

  1. 免费 CDN 玩法 —— 文件一键上传到 NPM

    前言 unpkg.jsdelivr 等站点可加速 NPM 包文件,适合作为个人网站或演示案例的免费 CDN. 虽然上传文件到 NPM 很简单,创建 package.json 然后 npm publis ...

  2. 论文解读丨表格识别模型TableMaster

    摘要:在此解决方案中把表格识别分成了四个部分:表格结构序列识别.文字检测.文字识别.单元格和文字框对齐.其中表格结构序列识别用到的模型是基于Master修改的,文字检测模型用到的是PSENet,文字识 ...

  3. 技术博客——微信小程序UI的设计与美化

    技术博客--微信小程序UI的设计与美化 在alpha阶段的开发过后,我们的小程序也上线了.看到自己努力之后的成果大家都很开心,但对比已有的表情包小程序,我们的界面还有很大的提升空间,许多的界面都是各个 ...

  4. 【好好编程-技术博客】微信小程序开发中前后端的交互

    微信小程序开发中前后端的交互 微信小程序的开发有点类似与普通网页的开发,但是也不尽然相同.小程序的主要开发语言是JavaScript,开发同普通的网页开发有很大的相似性,对于前端开发者而言,从网页开发 ...

  5. Noip模拟30 2021.8.4

    T1 毛一琛 考场上打的稳定的$O((2^n)^2)$的暴力.其实再回忆一下上次那道用二进制枚举的题$y$ 就可以知道一样的道理,使用$\textit{Meet In the Middle}$, 按照 ...

  6. 攻防世界 杂项 5.wireshark-1

    题目描述: 黑客通过wireshark抓到管理员登陆网站的一段流量包(管理员的密码即是答案). flag提交形式为flag{XXXX} 看到登录应该想到它是HTTP POST请求,wireshark搜 ...

  7. ST表 求 RMQ(区间最值)

    RMQ即Range Minimum/Maximun Query,中文意思:查询一个区间的最小值/最大值 比如有这样一个数组:A{3 2 4 5 6 8 1 2 9 7},然后问你若干问题: 数组A下标 ...

  8. 第10课 OpenGL 3D世界

    加载3D世界,并在其中漫游: 在这一课中,你将学会如何加载3D世界,并在3D世界中漫游.这一课使用第一课的代码,当然在课程说明中我只介绍改变了代码. 这一课是由Lionel Brits (βtelge ...

  9. Github图床设置

    创建新仓库 点击右上角加号->新建仓库,填写基本信息后点击下面的创建即可 https://github.com/new 创建新令牌 点击设置->开发者设置->私人令牌->生成新 ...

  10. element-UI 中的upload组件如何添加token?

    <el-upload :show-file-list="false" :on-error="errmsg" :headers="headers& ...