题目点这里
题解:
首先,我们会发现,在某一天全部买、全部卖一定比分散买卖更优,因为分散买的话我们可以把它们全部集中到最优的一天买卖,答案一定更优。
设f[i]为第i天卖出全部股票最多能得到的钱。
设第i天用f[i]的钱买x的B卷,rate[i]*x的A卷。
则
a
[
i
]
∗
r
a
t
e
[
i
]
∗
x
+
b
[
i
]
∗
x
=
f
[
i
]
a[i]*rate[i]*x+b[i]*x=f[i]
a[i]∗rate[i]∗x+b[i]∗x=f[i]
x
∗
(
a
[
i
]
∗
r
a
t
e
[
i
]
+
b
[
i
]
)
=
f
[
i
]
x*(a[i]*rate[i]+b[i])=f[i]
x∗(a[i]∗rate[i]+b[i])=f[i]
x
=
f
[
i
]
/
(
a
[
i
]
∗
r
a
t
e
[
i
]
+
b
[
i
]
)
x=f[i]/(a[i]*rate[i]+b[i])
x=f[i]/(a[i]∗rate[i]+b[i])
所以第i天最多有
r
a
t
e
[
i
]
∗
f
[
i
]
/
(
a
[
i
]
∗
r
a
t
e
[
i
]
+
b
[
i
]
)
rate[i]*f[i]/(a[i]*rate[i]+b[i])
rate[i]∗f[i]/(a[i]∗rate[i]+b[i])的A卷,
f
[
i
]
/
(
a
[
i
]
∗
r
a
t
e
[
i
]
+
b
[
i
]
)
f[i]/(a[i]*rate[i]+b[i])
f[i]/(a[i]∗rate[i]+b[i])的b卷。
如果第i天再卖出第j天的所有股票,中间不进行任何操作,全部所得利润为
a
[
i
]
∗
r
a
t
e
[
j
]
∗
f
[
j
]
/
(
a
[
j
]
∗
r
a
t
e
[
j
]
+
b
[
j
]
)
+
b
[
i
]
∗
f
[
j
]
/
(
a
[
j
]
∗
r
a
t
e
[
j
]
+
b
[
j
]
)
a[i]*rate[j]*f[j]/(a[j]*rate[j]+b[j])+b[i]*f[j]/(a[j]*rate[j]+b[j])
a[i]∗rate[j]∗f[j]/(a[j]∗rate[j]+b[j])+b[i]∗f[j]/(a[j]∗rate[j]+b[j])
令
x
[
i
]
=
r
a
t
e
[
i
]
∗
f
[
i
]
/
(
a
[
i
]
∗
r
a
t
e
[
i
]
+
b
[
i
]
)
x[i]=rate[i]*f[i]/(a[i]*rate[i]+b[i])
x[i]=rate[i]∗f[i]/(a[i]∗rate[i]+b[i]),
y
[
i
]
=
f
[
i
]
/
(
a
[
i
]
∗
r
a
t
e
[
i
]
+
b
[
i
]
)
y[i]=f[i]/(a[i]*rate[i]+b[i])
y[i]=f[i]/(a[i]∗rate[i]+b[i])
原式可以化简为
a
[
i
]
∗
x
[
j
]
+
b
[
i
]
∗
y
[
j
]
a[i]*x[j]+b[i]*y[j]
a[i]∗x[j]+b[i]∗y[j]
如果k>j在第i天卖出第k天的所有股票比第i天卖出第j天的所有股票更优且y[j] < y[k],则有
a
[
i
]
∗
x
[
k
]
+
b
[
i
]
∗
y
[
k
]
>
=
a
[
i
]
∗
x
[
j
]
+
b
[
i
]
∗
y
[
j
]
a[i]*x[k]+b[i]*y[k]>=a[i]*x[j]+b[i]*y[j]
a[i]∗x[k]+b[i]∗y[k]>=a[i]∗x[j]+b[i]∗y[j]
a
[
i
]
∗
(
x
[
k
]
−
x
[
j
]
)
>
=
b
[
i
]
∗
(
y
[
j
]
−
y
[
k
]
)
a[i]*(x[k]-x[j])>=b[i]*(y[j]-y[k])
a[i]∗(x[k]−x[j])>=b[i]∗(y[j]−y[k])
(
x
[
k
]
−
x
[
j
]
)
/
(
y
[
j
]
−
y
[
k
]
)
<
=
b
[
i
]
/
a
[
i
]
(x[k]-x[j])/(y[j]-y[k])<=b[i]/a[i]
(x[k]−x[j])/(y[j]−y[k])<=b[i]/a[i]
之后我们可以用cdq分治进行处理。
首先,我们对所有天以
b
[
i
]
/
a
[
i
]
b[i]/a[i]
b[i]/a[i]递增排序。然后进行分治。
设当前分治到左端点为l,右端点为r的连续的r-l+1天,mid=(l+r)/2。
如果l==r,可以直接先计算f[l]的值。
否则,我们将之前排好序的那个数组一分为二,第mid天及以前的丢左边,剩下的丢右边。两边分别仍然是b[i]/a[i]递增排序的。
接着,分治l~mid的区间。
在下一层分治结束后,我们已经将l~mid按y值递增排序了(这个一会儿再提为什么),
而且mid~r的b[i]/a[i]值也是递增排序的(这是分治l,mid之前的操作!)。
于是我们就可以直接把lmid的斜率丢进单调队列里面,然后更新mid+1r的答案。
(注意,mid+1~r的答案现在并没有确定,只是更新,一会儿分治(mid+1,r)的时候还会被更新到!
随着一次次的分治,你会发现f[i]都被任何
j
<
i
j<i
j<i的一天更新过答案!)
接着我们分治mid+1~r。
mid+1r回溯过来之后,我们将l,mid和mid+1r的y值进行一次归并排序,使得再回溯上一层的lmid或者mid+1r的y值是递增排序的。
代码:
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cassert>
using namespace std;
const int N=100005;
const double eps=1e-10;
int n,q[N];
double f[N];
struct data{
double a,b,rate,k,x,y;
int id;
friend bool operator < (const data &a,const data &b){
return a.k<b.k;
}
}a[N],b[N];
double slope(int j,int k){
if(fabs(a[j].y-a[k].y)<eps){
return a[k].x-a[j].x>0?-1e18:1e18;
}
return (a[k].x-a[j].x)/(a[j].y-a[k].y);
}
void solve(int l,int r){
if(l==r){
f[l]=max(f[l-1],f[l]);
a[l].y=f[l]/(a[l].a*a[l].rate+a[l].b);
a[l].x=a[l].rate*a[l].y;
return;
}
int mid=(l+r)/2,j=l,k=mid+1;
for(int i=l;i<=r;i++){
if(a[i].id<=mid){
b[j++]=a[i];
}else{
b[k++]=a[i];
}
}
for(int i=l;i<=r;i++){
a[i]=b[i];
}
solve(l,mid);
j=1,k=0;
for(int i=l;i<=mid;i++){
while(j<k&&slope(q[k],i)<slope(q[k-1],q[k])){
k--;
}
q[++k]=i;
}
for(int i=mid+1;i<=r;i++){
while(j<k&&slope(q[j],q[j+1])<a[i].k){
j++;
}
if(j<=k){
f[a[i].id]=max(f[a[i].id],a[i].a*a[q[j]].x+a[i].b*a[q[j]].y);
}
}
solve(mid+1,r);
j=l,k=mid+1;
for(int i=l;i<=r;i++){
if(j<=mid&&(k>r||a[j].y<a[k].y)){
b[i]=a[j++];
}else{
b[i]=a[k++];
}
}
for(int i=l;i<=r;i++){
a[i]=b[i];
}
}
int main(){
scanf("%d%lf",&n,&f[0]);
for(int i=1;i<=n;i++){
scanf("%lf%lf%lf",&a[i].a,&a[i].b,&a[i].rate);
a[i].k=a[i].b/a[i].a;
a[i].id=i;
}
sort(a+1,a+n+1);
solve(1,n);
printf("%.3lf\n",f[n]);
return 0;
}