P4027 [NOI2007]货币兑换

算法:dp+斜率优化

题面十分冗长,题意大概是有一种金券每天价值会有变化,你可以在某些时间点买入或卖出所有的金券,问最大收益

根据题意,很容易列出朴素的状态转移方程:

设\(f_i\)为第\(i\)天B券的数量,\(ans_j\)为以当前价格卖光第\(j\)天的金券可获得的收益,则

\(f_i=\max{ans_j}/(a_i*r_i+b_i)\)

\(O(n)\)求\(\max{ans_j}\),复杂度为\(O(n^2)\)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
typedef long long LL;
const int MAXN=1024*100;
int N;
double S,a[MAXN],b[MAXN],r[MAXN],f[MAXN],ans;
int main(){
scanf("%d%lf",&N,&S);
for(int i=1;i<=N;i++){
scanf("%lf%lf%lf",&a[i],&b[i],&r[i]);
}
ans=S;
f[1]=ans*r[1]/(a[1]*r[1]+b[1]);
for(int i=1;i<=N;i++){
for(int j=1;j<i;j++){
ans=max(ans,f[j]*a[i]+f[j]/r[j]*b[i]);
}
f[i]=ans*r[i]/(a[i]*r[i]+b[i]);
}
printf("%.3lf",ans);
return 0;
}

然而此题要求\(O(nlogn)\)的做法,故朴素的dp无法AC,此时可以想到斜率优化

step1:转化方程

设\(a_i\)为第\(i\)天A券的价格,\(b_i\)为第\(i\)天B券的价格,\(ca_i\)为第\(i\)天A券的数量,\(cb_i\)为第\(i\)天B券的数量,\(f_i\)为第\(i\)天的最大收益

则\(f_i=\max{ca_j*a_i+cb_j*b_i}\)

∴如果j比k更优,有

\(ca_j*a_i+cb_j*b_i>ca_k*a_i+cb_k*b_i\)

∴\((cb_j-cb_k)*b_i>-a_i*(ca_j-ca_k)\)

∴\(\frac{cb_j-cb_k}{ca_j-ca_k}>-\frac{a_i}{b_i}\)

以\(ca\)为横坐标\(cb\)为纵坐标建立如图所示平面直角坐标系

剩下的就可以-斜率优化-

不过此题不比模板题,ca与cb不满足单调性,所以需要用平衡树或cdq等方法维护,此处用stl_set维护(因为是凸壳,所以斜率与横坐标同时满足单调性,可以用一个关键字查找)

此处切线只要找\(-\frac{a}{b}\)的lower_bound即可(代码中的query)

此处插入点要把当前位置两边的点都判断一下是否与上凸壳冲突,删除(代码中的insert)

实现就不难了

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#include<set>
using namespace std;
typedef long long LL;
const int MAXN=1024*100;
int N;
double S,A[MAXN],B[MAXN],R[MAXN],F[MAXN],CA[MAXN],CB[MAXN];
inline double ABS(double x) { return x>0?x:-x; }
struct node {
double X,Y,K;
int flag;
node() { X=Y=K=flag=0; }
node(double x,double y) { X=x; Y=y; flag=0; }
friend inline bool operator<(node x,node y) {
if(x.flag||y.flag) { return x.K>y.K; }
return x.X<y.X;
}
friend inline bool operator==(node x,node y) { return ABS(x.X-y.X)<1e-8; }
friend inline double operator*(node x,node y) { return (y.Y-x.Y)/(y.X-x.X); }//斜率
inline bool error() { return X<-1e20||Y<-1e20; }
} error(-1e21,-1e21);
set<node> dq;
typedef set<node>::iterator ITER;
inline node next(node x) {
ITER ii=dq.upper_bound(x);
return ii==dq.end()?error:*ii;
}
inline node lower(node x) {
ITER ii=dq.lower_bound(x);
return ii==dq.end()?error:*ii;
}
inline node pre(node x) {
ITER ii=dq.lower_bound(x);
return ii==dq.begin()?error:*(--ii);
}
inline void insert(node x) {
if(dq.empty()) {
x.K=0;
dq.insert(x);
return;
}
node L=pre(x),R=lower(x);
if((L.error()&&x.Y<R.Y)||(!L.error()&&!R.error()&&L*x-L*R<1e-8)/**/||(x==R)) { return; }
R=next(x);
while(1) {
L=R;
R=next(L);
if(L.error()||R.error()||(x*L)-(L*R)>=1e-8) { break; }
dq.erase(L);
}
L=pre(x);
while(1) {
R=L;
L=pre(R);
if(L.error()||R.error()||(L*R)-(R*x)>=1e-8) { break; }
dq.erase(R);
}
L=pre(x);
R=next(x);
x.K=(L.error()?0:(L*x));
dq.insert(x);
if(!R.error()) {
dq.erase(R);
R.K=(x*R);
dq.insert(R);
}
}
inline double query(double x,double y) {
node ii;
ii.flag=1;
ii.K=-x/y;
ii=*(--dq.lower_bound(ii));
return ii.error()?0:ii.X*x+ii.Y*y;
}
int main() {
scanf("%d%lf",&N,&S);
for(int i=1; i<=N; i++) {
scanf("%lf%lf%lf",&A[i],&B[i],&R[i]);
}
F[1]=S;
CB[1]=S/(A[1]*R[1]+B[1]);
CA[1]=CB[1]*R[1];
insert(node(CA[1],CB[1]));
for(int i=2; i<=N; i++) {
F[i]=max(F[i-1],query(A[i],B[i]));
CB[i]=F[i]/(A[i]*R[i]+B[i]);
CA[i]=CB[i]*R[i];
insert(node(CA[i],CB[i]));
}
printf("%.3lf",F[N]);
return 0;
}

洛谷P4027 [NOI2007]货币兑换的更多相关文章

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

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

  2. LOJ 2353 & 洛谷 P4027 [NOI2007]货币兑换(CDQ 分治维护斜率优化)

    题目传送门 纪念一下第一道(?)自己 yy 出来的 NOI 题. 考虑 dp,\(dp[i]\) 表示到第 \(i\) 天最多有多少钱. 那么有 \(dp[i]=\max\{\max\limits_{ ...

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

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

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

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

  5. 洛谷 P2047 [NOI2007]社交网络 解题报告

    P2047 [NOI2007]社交网络 题目描述 在社交网络(\(social\) \(network\))的研究中,我们常常使用图论概念去解释一些社会现象.不妨看这样的一个问题.在一个社交圈子里有\ ...

  6. 洛谷——P2047 [NOI2007]社交网络

    P2047 [NOI2007]社交网络 $Floyd$,一眼看到就是他(博主是不小心瞄到了这个题的标签吧qwq) 这个题目只要预处理出$S$到$T$的最短路的条数即可,类似$Spfa$的更新方法 如果 ...

  7. 洛谷P2047 [NOI2007]社交网络 [图论,最短路计数]

    题目传送门 社交网络 题目描述 在社交网络(social network)的研究中,我们常常使用图论概念去解释一些社会现象.不妨看这样的一个问题.在一个社交圈子里有n个人,人与人之间有不同程度的关系. ...

  8. P4027 [NOI2007]货币兑换

    传送门 首先有一个显然的贪心,每次操作都要做到底,为了最优不会出现只卖一部分或者只买一部分的操作 所以设 $f[i]$ 表示前 $i$ 天得到的最大价值,那么对于每一个 $i$,枚举所有 $j< ...

  9. BZOJ1491 洛谷2047 NOI2007 社交网络

    Description: 在社交网络(social network)的研究中,我们常常使用图论概念去解释一些社会现象.不妨看这样的一个问题.在一个社交圈子里有n个人,人与人之间有不同程度的关系.我 们 ...

随机推荐

  1. BeanShell Sampler生成uuid

  2. git 去除本地所有没有保存的修改

    git 去除本地所有没有保存的修改,参考How do I revert all local changes in Git managed project to previous state? 知道运行 ...

  3. mysql双主热备

    先搭建mysql主从模式,主从请参考mysql 主从笔记 然后在在配置文件里添加如下配置 1 log_slave_updates= #双主热备的关键参数.默认情况下从节点从主节点中同步过来的修改事件是 ...

  4. thinkphp 范围标签

    范围判断标签包括in notin between notbetween四个标签,都用于判断变量是否中某个范围. 大理石平台价格 IN和NOTIN 用法: 假设我们中控制器中给id赋值为1: $id = ...

  5. 最大流dicnic——hdu1532模板题

    #include<bits/stdc++.h> using namespace std; #define maxn 1005 #define ll long long const ll i ...

  6. hadoop新增新数据节点和退役数据节点

    新增数据节点 0. 需求随着公司业务的增长,数据量越来越大,原有的数据节点的容量已经不能满足存储数据的需求,需要在原有集群基础上动态添加新的数据节点.1. 环境准备 (1)在hadoop03主机上再克 ...

  7. CF1086F Forest Fires

    CF1086F Forest Fires 有点意思的题目 直接统计每个格子的val是非常难办的.很难知道每秒新出来多少个格子 设$F[i]$表示,前i时刻覆盖的格子的数量 则,$ans=\sum_{i ...

  8. MySql查询分页数据

    MySql查询分页数据

  9. 云-腾讯云-云直播:云直播(LVB)

    ylbtech-云-腾讯云-云直播:云直播(LVB) 云直播(Live Video Broadcasting,LVB)依托腾讯多年的音视频技术平台,以及全球海量加速节点和领先的音视频 AI 技术,为开 ...

  10. EJB(Enterprise JavaBean)科普

    该文章是引用的,主要用于自己的学习,然后是记载免得忘记的时候到处乱找.结尾有引用地址. 到底EJB是什么?被口口相传的神神秘秘的,百度一番,总觉得没有讲清楚的,仍觉得一头雾水.百度了很久,也从网络的文 ...