这个题首先要注意到提示:全买全卖,,不然很容易误入歧途
然后可以根据确定性n^2 dp,,由于它是全买全卖,所以枚举上一个买入的点在哪里,算全卖出的价值取max
如果这个点不卖,则它一定有钱,所以继承前面最大的钱取max,
然后考虑在这个点卖,由于a:b是定值,钱和数量是直接相关的 所以只记录一个b的数量即可
f【i】=max(f【i-1】,A【j】*fa【i】+B【j】*fb【i】)
用斜率优化的方式化式子:
-A【j】=fa【i】/ fb【i】 *B【j】*rate【j】 - f【i】/fb【i】
y = k x + b
注意正负号,第一次式子写错了调了2h、
然后就需要x、k的单调性,就可以用cdq分治
对左区间操作 对整体操作 对右区间操作 ←顺序非常重要
由于y是负值 ,b是负值 , f【i】∝ -b
所以要让-b最大, b就要最小
由于x、k单增,所以最优点一定在下凸包上
所以左区间按x排序 右区间按k排序,转移即可
注意排完之后要按时间顺序还原
复杂度 o(nlog^2n)
码(常数巨大,超时卡过):
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
double ans;
int n,m,i,j,k,q[100005],s;
struct tian
{
double fa,fb,id,R,k,B,f;
}d[100005];
bool cmp1(tian a,tian b)
{
return a.id<b.id;
}
bool cmp2(tian a,tian b)
{
return a.B*a.R<b.B*b.R;
}
bool cmp3(tian a,tian b)
{
return a.fa/a.fb<b.fa/b.fb;
}
double xl(int a,int b)
{
return (-d[a].B+d[b].B)/(d[a].B*d[a].R-d[b].B*d[b].R);
}
void cdq(int l,int r)
{
if(l==r)
{
if(d[l].f<d[l-1].f)
{
d[l].f=d[l-1].f;
d[l].B=d[l].f/(d[l].R*d[l].fa+d[l].fb);
}
return;
}
int mid=(l+r)>>1;
cdq(l,mid);
for(i=l;i<=mid;i++)
if(d[i].f<d[i-1].f)
{
d[i].f=d[i-1].f;
d[i].B=d[i].f/(d[i].R*d[i].fa+d[i].fb);
}
sort(d+l,d+mid+1,cmp2);
sort(d+mid+1,d+r+1,cmp3);
int z1=1,z2=0;
for(i=l;i<=mid;i++)
{
while(z1<z2&&xl(q[z2-1],i)>=xl(q[z2],i))z2--;
q[++z2]=i;
}
for(i=mid+1;i<=r;i++)
{
while(z1<z2&&d[q[z1]].B*d[i].fb+d[q[z1]].B*d[q[z1]].R*d[i].fa<=d[q[z1+1]].B*d[i].fb+d[q[z1+1]].B*d[q[z1+1]].R*d[i].fa)z1++;
if(d[i].f<d[q[z1]].B*d[i].fb+d[q[z1]].B*d[q[z1]].R*d[i].fa)
{
d[i].f=d[q[z1]].B*d[i].fb+d[q[z1]].B*d[q[z1]].R*d[i].fa;
d[i].B=d[i].f/(d[i].R*d[i].fa+d[i].fb);
}
}
sort(d+l,d+mid+1,cmp1);
sort(d+mid+1,d+r+1,cmp1);
//消除影响,按实际安排
cdq(mid+1,r);
}
int main()
{
scanf("%d%d",&n,&s);
d[0].f=s;
for(i=1;i<=n;i++)
{
scanf("%lf%lf%lf",&d[i].fa,&d[i].fb,&d[i].R);
d[i].id=i;
d[i].k=d[i].fa/d[i].fb;
}
cdq(1,n);
ans=d[i].f;
for(i=2;i<=n;i++)
{
ans=max(ans,d[i].f);
}
printf("%.3lf",ans);
}