先%CDQ…
首先需要分析出一个性质,如果某天决定买入,那么一定花掉当前拥有的所有钱,如果某天决定卖出,那么一定卖掉拥有的所有股票。
于是对dp方程推一推倒一倒啊就是经典的斜率优化了…
(其实一个月前做的题我现在懒得推了…>_<…为啥我还要写这篇博客?为啥平衡树维护凸包没人用set写呢!?)
set:(边界判起来真麻烦…>_<…不过比手写平衡树要好不少…)
#include<set>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define dl long double
#define lb lower_bound
//by:MirrorGray
using namespace std;
const int N=211111;
const dl eps=1e-25;
int D;
dl dp[N],ans;
double A[N],B[N],R[N];
struct node{
dl x,y;int id;
node(dl a=0,dl b=0,int c=0){
x=a;y=b;id=c;
}
};
multiset <node> s;
inline dl C(int i){
return dp[i]/(A[i]*R[i]+B[i]);
}
inline dl rake(int x,int y){
if(fabs(C(x)-C(y))<eps)return 1e18;
return (C(x)*R[x]-C(y)*R[y])/(C(y)-C(x));
}
bool operator <(const node&a,const node&b){
if(~D&1){
if(a.x==b.x)return a.y-b.y<-eps;
return a.x-b.x<-eps;
}
else{
if(a.y==b.y)return a.x-b.x<-eps;
return a.y-b.y<-eps;
}
}
int main(){
int n,ss;scanf("%d%d",&n,&ss);ans=ss;
for(int i=1;i<=n;i++)scanf("%lf%lf%lf",&A[i],&B[i],&R[i]);
dp[1]=ans;s.insert(node(C(1),1e18,1));
for(int i=2;i<=n;i++){
D=true;
node it=*s.lb(node(0,B[i]/A[i],0));
ans=dp[i]=max(ans,dp[it.id]/((dl)A[it.id]*R[it.id]+B[it.id])*((dl)A[i]*R[it.id]+B[i]));
D=false;node tmp(C(i),0,i);
while(s.lb(tmp)!=s.end()){
if(s.lb(tmp)==--s.end())break;
it=*s.lb(tmp);
if(rake(i,it.id)+eps<it.y)break;
s.erase(s.lb(tmp));
}
while(true){
if(--s.lb(tmp)==s.begin())break;
node t1=*--s.lb(tmp);it=*(--(--s.lb(tmp)));
if(it.y>rake(t1.id,i)+eps)s.erase(--s.lb(tmp));
else break;
}
if(s.lb(tmp)==s.end())tmp.y=1e18;else tmp.y=rake(i,s.lb(tmp)->id);
if(s.lb(tmp)!=s.begin()){
it=*--s.lb(tmp);
if(it.y>tmp.y)continue;
s.erase(--s.lb(tmp));
it.y=rake(it.id,i),s.insert(it);
}
s.insert(tmp);
}
printf("%.3f\n",(double)ans);
return 0;
}
CDQ分治:
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define dl long double
//by:MirrorGray
using namespace std;
const int N=211111;
const dl eps=1e-20;
//重新理一下思路,先归并排序使得B[i]/A[i]单调,分治维护凸包
dl dp[N];
double A[N],B[N],R[N];
int s[N],ra[N][21],fz[N],a[N];
void sort(int l,int r,int d){
if(l==r){
ra[l][d]=l;return ;
}
int mid=(l+r)>>1;
sort(l,mid,d+1);sort(mid+1,r,d+1);
int t1=l,t2=mid+1,t=l;
while(t<=r){
if(t2>r||(t1<=mid&&B[ra[t1][d+1]]/A[ra[t1][d+1]]-B[ra[t2][d+1]]/A[ra[t2][d+1]]<-eps))
ra[t++][d]=ra[t1++][d+1];
else ra[t++][d]=ra[t2++][d+1];
}
}
inline dl C(int i){
return dp[i]/((dl)A[i]*R[i]+B[i]);
}
inline dl rake(int x,int y){
if(fabs(C(x)-C(y))<eps)return 1e18;
return (C(x)*R[x]-C(y)*R[y])/(C(y)-C(x));
}
void solve(int l,int r,int d){
if(l==r){
fz[l]=l;
dp[l]=max(dp[l-1],dp[l]);
return ;
}
int mid=(l+r)>>1;
solve(l,mid,d+1);
int top=0;
for(int i=l;i<=mid;i++){
while(top>1&&rake(fz[s[top]],fz[i])<rake(fz[s[top-1]],fz[s[top]])-eps)top--;
//为什么我总是把符号弄反真是搞不懂…
s[++top]=i;
}
int j=1;
for(int i=mid+1;i<=r;i++){
while(j<top&&rake(fz[s[j]],fz[s[j+1]])-B[ra[i][d]]/A[ra[i][d]]<-eps)j++;
dp[ra[i][d]]=max(dp[ra[i][d]],C(fz[s[j]])*(A[ra[i][d]]*R[fz[s[j]]]+B[ra[i][d]]));
}
solve(mid+1,r,d+1);
int t1=l,t2=mid+1,t=l;
while(t<=r){
if(t2>r||(t1<=mid&&C(fz[t1])<C(fz[t2])-eps))a[t++]=fz[t1++];
else a[t++]=fz[t2++];
}
for(int i=l;i<=r;i++)fz[i]=a[i];
}
int main(){
int n,s;scanf("%d%d",&n,&s);
for(int i=1;i<=n;i++)scanf("%lf%lf%lf",&A[i],&B[i],&R[i]);
sort(1,n,0);
dp[1]=s;solve(1,n,1);
printf("%.3f\n",(double)dp[n]);
return 0;
}