我们可以很快发现一个dp解法:可以发现答案一定是由所持有的卷的数目计算出来的,而卷的数目是可以转移的,所以令 f[i] 表示在第i天可以得到的B卷的数目的最大值,可以得到转移方程为: f[i]=max{ans,Rate[j]∗f[j]∗a[i]+f[j]∗b[i]}Rate,ans=max{ans,Rate[j]∗f[j]∗a[i]+f[j]∗b[i]}
分析对于i的两个决策j和k,可以发现决策j比决策k优当且仅当:
f[j]∗Rate[j]−f[k]∗Rate[k]f[j]−f[k]>−b[i]a[i]
时,决策j比k优。
我们定义
slope(j,k)
表示这个式子的左侧,
G[i]=f[i]∗Rate[i]
。
即,
slope(j,k)>−b[i]a[i]
,在二维平面上定义点
Xi=(fi,Gi)
,那么式子左侧就是个两个点之间的斜率。
我们就可以斜率优化啦:
1)
给一个点集中添加一个点。
2)
给定一个负数斜率
K
,询问所有斜率为
dp的第二种解法:
我们令
f[i]
表示最优答案,
x[i],y[i]
分别表示两种卷在i时刻的最大数量。
于是
f[i]=max{f[i−1],x[j]∗a[i]+y[j]∗b[i]}
接着我们考虑如何斜率优化,
y[j]=(−a[i]b[i]∗x[j]+f[i]b[i])
,我们把
−a[i]b[i]
看做斜率,观察发现,对于平面上的点(x,y)来说,要使得
f[i]
最大,我们需要使得直线在y轴上的截距最大。
于是我们维护平面上的点的上凸包。
解法1:Splay
用splay把所有点维护起来,每个点记录和左右两边的点的斜率。
对于每次的询问,我们用一条斜率为
−a[i]b[i]
的直线去切割这个凸壳,就可以找到最优的转移点。
对于每次插入点,首先取出
x[j]
,然后向两边找到它能作为凸包时需要连接的点,删去中间经过的点。若找不到,也就说明它在凸包内,是一个凹点,把它自己删除。
//注意下面代码为了写起来方便
x,y
与上面的方程解释是反的
//坑填好啦
//你问我CDQ在哪里? 当然是选择坑着啦
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
const double eps=1e-8;
const double inf=1e20;
double X[maxn],Y[maxn];
//splay
int fa[maxn],ch[maxn][2];
double lk[maxn],rk[maxn];
int root,sz;
//splay
inline bool get(int x){
return (ch[fa[x]][1]==x);
}
inline void rotate(int x){
int old=fa[x];int oldf=fa[old];int d=get(x);
fa[ch[x][d^1]]=old;ch[old][d]=ch[x][d^1];
fa[old]=x;ch[x][d^1]=old;
fa[x]=oldf;
if(oldf)
ch[oldf][ch[oldf][1]==old]=x;
}
inline void splay(int x,int g=0){
for(int y;(y=fa[x])!=g;rotate(x)){
if(fa[y]!=g)
rotate((get(x)==get(y))? y:x);
}
if(!g)
root=x;
}
inline double getk(int j,int k){
if(fabs(X[j]-X[k]<=eps)) return inf;
return (Y[j]-Y[k])/(X[j]-X[k]);
}
inline int get_pre(){
int p=ch[root][0],ret=p;
while(p){
if(getk(root,p)+eps>=lk[p])
p=ch[p][0];
else
ret=p,p=ch[p][1];
}
return ret;
}
inline int get_suc(){
int p=ch[root][1],ret=p;
while(p){
if(getk(p,root)<=rk[p]+eps)
p=ch[p][1];
else
ret=p,p=ch[p][0];
}
return ret;
}
inline void insert(int &r,int pre,int p){
if(!r){
r=p;
fa[p]=pre;
return;
}
if(X[p]<=X[r]+eps) insert(ch[r][0],r,p);
else insert(ch[r][1],r,p);
}
inline void update(int p){
splay(p);
if(ch[p][0]){
int l=get_pre();
splay(l,p);
ch[l][1]=0;
lk[p]=rk[l]=getk(p,l);
}else lk[p]=inf;
if(ch[p][1]){
int r=get_suc();
splay(r,p);
ch[r][0]=0;
rk[p]=lk[r]=getk(r,p);
}else rk[p]=-inf;
//删除凹点
if(lk[p]<rk[p]+eps){
root=ch[p][0];
ch[root][1]=ch[p][1];
fa[ch[p][1]]=root;
fa[root]=0;
rk[root]=lk[ch[p][1]]=getk(ch[root][1],root);
}
}
inline int getpos(double k){
int p=root;
while(p){
//切割到了凸包
if(lk[p]+eps>=k&&k+eps>=rk[p])
break;
if(lk[p]<k+eps)
p=ch[p][0];
else p=ch[p][1];
}
return p;
}
inline double getans(double a,double b){
int p=getpos(-b/a);
return Y[p]*a+X[p]*b;
}
double a,b,rate;
int n;double ans;
int main(int argc,const char * argv[]){
scanf("%d%lf",&n,&ans);
for(int i=1;i<=n;i++){
scanf("%lf%lf%lf",&a,&b,&rate);
ans=max(ans,getans(a,b));
X[i]=ans/(a*rate+b);
Y[i]=X[i]*rate;
insert(root,0,i);//将X,Y所代表的点插入平衡树
update(i);
}
printf("%.3lf",ans);
return 0;
}