[NOI2007] 货币兑换

设 $f_i$ 表示第 $i$ 天最大的收益。

初始条件 $f_0=s$,答案 $f_n$。

转移枚举上一个全买券的点 $j$,设 $y$ 表示第 $j$ 天买 $B$ 的数量,解方程。

$Rate_j\times y_j\times a_j+y_j\times b_j=f_j$

得到 $y_j=\large\frac{f_j}{Rate_j\times a_j+b_j}$

设 $x$ 表示买 $A$ 的数量,显然 $x_i=Rate_j\times y_i$

转移方程 $f_i=\max(f_{i-1},\max{a_i\times x_j+b_i\times y_j | j\in[1,i) } )$

提出 $b_i$,得到 $f_i=\max(f_{i-1},b_i\times \max{\large\frac{a_i}{b_i}\small\times x_j+y_j})$

发现变成一次函数形式,即对于每个 $x_j,y_j$,查询 $\large\frac{a_i}{b_i}$ 处的最大值即可。

李超线段树即可(这里是一个简洁的李超树板子 qwq)。

#include<bits/stdc++.h>
using namespace std;
#define db double
#define endl '\n'
const int N=1e5+10;
int n;
db s;
db x[N],y[N],k[N],c[N];
db f[N],a[N],b[N],r[N];
int t[N<<2];
db calc(int i,int p){return x[i]*k[p]+y[i];}
void update(int p,int l,int r,int x){
if(l==r){
if(calc(t[p],l)<calc(x,l)) t[p]=x;
return ;
}
int mid=(l+r)>>1;
if(calc(t[p],mid)<calc(x,mid)) swap(t[p],x);
if(calc(t[p],l)<calc(x,l)) update(p<<1,l,mid,x);
else update(p<<1|1,mid+1,r,x);
}
db query(int p,int l,int r,int x){
if(l==r) return calc(t[p],x);
int mid=(l+r)>>1;
db res=calc(t[p],x);
if(x<=mid) return max(res,query(p<<1,l,mid,x));
return max(res,query(p<<1|1,mid+1,r,x));
}
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin>>n>>s;
f[0]=s;
for(int i=1;i<=n;++i){
cin>>a[i]>>b[i]>>r[i];
k[i]=c[i]=a[i]/b[i];
}
sort(k+1,k+1+n);
for(int i=1;i<=n;++i){
int u=lower_bound(k+1,k+1+n,c[i])-k;
f[i]=max(f[i-1],b[i]*query(1,1,n,u));
db p=a[i]*r[i]+b[i];
y[i]=f[i]/p;
x[i]=y[i]*r[i];
update(1,1,n,i);
}
cout<<fixed<<setprecision(3)<<f[n]<<endl;
return 0;
}