正题
题目链接:https://www.luogu.com.cn/problem/P4027
题目大意
nnn天开始时有SSS元钱,每天AAA种股票价格为aia_iai,BBB种价格为bib_ibi。然后出售必须AAA和BBB出售相同比例,买入时AAA和BBB必须按照rir_iri的比例买入。
求最后的钱最多是多少
解题思路
首先考虑dpdpdp。
假设有情况是只出售一部分最赚,然后之后(中间不买入)再出售一部分能更赚钱,那么将前面的放在最后出售的那一天出售完显然不会更亏,所以显然不成立,所以每次出售必定是全部出售完。同理,每次买入也是全部买入。
那么我们可以设fif_ifi表示第iii天的最多钱数,那么全部买入时AAA股的数量为xi=firiairi+bix_i=\frac{f_ir_i}{a_ir_i+b_i}xi=airi+bifiri,BBB股数量为yi=fiairi+biy_i=\frac{f_i}{a_ir_i+b_i}yi=airi+bifi。有方程式fi=max{fi−1,xjai+yjbi}f_i=max\{f_{i-1},x_ja_i+y_jb_i\}fi=max{fi−1,xjai+yjbi}
然后考虑优化方程fi=xjai+yjbif_i=x_ja_i+y_jb_ifi=xjai+yjbi
⇒yj=−aibixj+fibi\Rightarrow y_j=-\frac{a_i}{b_i}x_j+\frac{f_i}{b_i}⇒yj=−biaixj+bifi
有一条斜率为−aibi-\frac{a_i}{b_i}−biai经过决策点(xj,yj)(x_j,y_j)(xj,yj)
因为bib_ibi固定,所以是要求截距fibi\frac{f_i}{b_i}bifi最大,那么显然每个可能的决策点(xj,yj)(x_j,y_j)(xj,yj)构成一个上凸壳。(斜率递减)
若已经构建了上凸壳,那么如何求答案,首先我们要找到第一个位置使得该决策点的前面的边的斜率大于−aibi-\frac{a_i}{b_i}−biai,后面那条边的斜率小于−aibi-\frac{a_i}{b_i}−biai即可。
由于都不是单调的,所以我们可以用CDQCDQCDQ分治。
每次计算完[l,mid][l,mid][l,mid]后我们可以用前面的[l,mid][l,mid][l,mid]用单调栈储存凸壳,然后让[mid+1,r][mid+1,r][mid+1,r]保持−aibi-\frac{a_i}{b_i}−biai递增,然后即可双指针寻找答案。
时间复杂度O(nlog2n)O(n\log^2 n)O(nlog2n)(如果用归并排序好像可以做到O(nlogn)O(n\log n)O(nlogn)
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int N=1e5+10;
int n,p[N],q[N],s[N],tot;
double f[N],x[N],y[N],ra[N],a[N],b[N];
double slope(int a,int b)
{if(fabs(x[a]-x[b])<1e-9)return 1e9;return (y[a]-y[b])/(x[a]-x[b]);
}
bool cmp(int i,int j)
{return a[i]/b[i]>a[j]/b[j];}
bool cMp(int i,int j)
{return x[i]<x[j];}
void cdq(int l,int r){if(l==r){f[l]=max(f[l-1],f[l]);x[l]=f[l]*ra[l]/(a[l]*ra[l]+b[l]);y[l]=f[l]/(a[l]*ra[l]+b[l]);return;}int mid=(l+r)>>1,t1=l,t2=mid+1;for(int i=l;i<=r;i++)if(p[i]<=mid)q[t1++]=p[i];else q[t2++]=p[i];for(int i=l;i<=r;i++)p[i]=q[i];cdq(l,mid);tot=0;for(int i=l;i<=mid;i++){while(tot>1&&slope(s[tot],p[i])>slope(s[tot-1],s[tot]))tot--;s[++tot]=p[i];}for(int i=mid+1;i<=r;i++){while(tot>1&&slope(s[tot-1],s[tot])<-a[p[i]]/b[p[i]])tot--;int pos=s[tot];f[p[i]]=max(f[p[i]],x[pos]*a[p[i]]+y[pos]*b[p[i]]);}cdq(mid+1,r);sort(p+l,p+1+r,cMp);
}
int main()
{scanf("%d%lf",&n,&f[0]);for(int i=1;i<=n;i++)scanf("%lf%lf%lf",&a[i],&b[i],&ra[i]),p[i]=i;sort(p+1,p+1+n,cmp);cdq(1,n);printf("%.3lf",f[n]);
}