题目链接:https://www.luogu.com.cn/problem/P4027
典型的线性
d
p
dp
dp
题目最底下居然还有提示
d
p
[
i
]
dp[i]
dp[i]在第
i
i
i天之前买光股票的最大收入
d
p
[
i
]
=
m
a
x
(
d
p
[
j
]
∗
r
a
t
e
j
r
a
t
e
j
∗
a
j
+
b
j
∗
a
i
+
d
p
[
j
]
r
a
t
e
j
∗
a
j
+
b
j
∗
b
i
)
dp[i]=max(\frac{dp[j]*rate_j}{rate_j*a_j+b_j}*a_i+\frac{dp[j]}{rate_j*a_j+b_j}*b_i)
dp[i]=max(ratej∗aj+bjdp[j]∗ratej∗ai+ratej∗aj+bjdp[j]∗bi)
式子中带乘积,可以考虑斜率优化(很好写成
Y
=
K
x
+
B
Y=Kx+B
Y=Kx+B的形式)
令:
d
p
[
j
]
∗
r
a
t
e
j
r
a
t
e
j
∗
a
j
+
b
j
=
−
x
\frac{dp[j]*rate_j}{rate_j*a_j+b_j}=-x
ratej∗aj+bjdp[j]∗ratej=−x
d
p
[
j
]
r
a
t
e
j
∗
a
j
+
b
j
=
Y
\frac{dp[j]}{rate_j*a_j+b_j}=Y
ratej∗aj+bjdp[j]=Y
d
p
[
i
]
=
B
dp[i]=B
dp[i]=B
则:
Y
=
a
[
i
]
b
[
i
]
x
+
B
Y=\frac{a[i]}{b[i]}x+B
Y=b[i]a[i]x+B
就可以斜率优化了
但这道题比较变态
要维护动态凸包
可以用平衡树维护
按
x
x
x坐标排序,每次查找最值和动态往里加点
200行fhqtreap
C o d e Code Code
#include <bits/stdc++.h>
using namespace std;
const int MAXN=1e5,Mod=1e9+7;
const double esp=1e-6;
struct Point{
double x,y;
inline Point(double xx=0,double yy=0){x=xx,y=yy;}
};
inline double dcmp(double);
inline bool operator <= (const Point &X,const Point &Y){return dcmp(X.x-Y.x)<=0;}
inline bool operator > (const Point &X,const Point &Y){return X.x > Y.x;}
struct node{
int rank,lc,rc,size;
Point val;
inline node(){lc=rc=0;}
}tree[MAXN+10];
struct w{double a,b,rate;}a[MAXN+10];
double dp[MAXN+10];
int tot=0;
inline double slope(Point,Point);
int Query(int,double);
void Insert(int&,Point);
namespace fhqtreap{
inline void update(int);
inline int add_node(Point);
void spilt(int,int&,int&,Point);
void merge(int&,int,int);
void Insert(int&,Point);
void Del_all(int&,Point);
Point get_kth(int,int);
Point get_pre(int&,Point);
Point get_nx(int&,Point);
}
int main(){
//freopen ("std.in","r",stdin);
//freopen ("std.out","w",stdout);
srand((unsigned)time(NULL));
int n,s,rt=0;
scanf("%d%d",&n,&s);
for (register int i=1;i<=n;++i) scanf("%lf%lf%lf",&a[i].a,&a[i].b,&a[i].rate);
dp[0]=s;
for (register int i=1;i<=n;++i){
dp[i]=dp[i-1];
double k=a[i].a/a[i].b;
if (rt){
int j=Query(rt,k);
Point p=tree[j].val;
dp[i]=max(dp[i],-p.x*a[i].a+p.y*a[i].b);
}
double x=-dp[i]*a[i].rate/(a[i].rate*a[i].a+a[i].b);
double y=dp[i]/(a[i].rate*a[i].a+a[i].b);
Insert(rt,Point(x,y));
}
printf("%.3f\n",dp[n]);
return 0;
}
inline double dcmp(double x){return fabs(x)<esp? 0 : x;}
inline double slope(Point x,Point y){return (x.y-y.y)/(x.x-y.x);}
void Insert(int &rt,Point x){
using namespace fhqtreap;
if (!rt){
fhqtreap::Insert(rt,x);
return;
}
Point y=get_pre(rt,x),l;
Point z=get_nx(rt,x),r;
if (y.y!=-Mod && z.y!=-Mod && dcmp(slope(y,x)-slope(z,x))<=0) return;
while (1){
if (y.x == -Mod && y.y == -Mod) break;
l=get_pre(rt,y);
if (l.x==-Mod && l.y==-Mod || dcmp(slope(l,y) - slope(y,x)) > 0) break;
Del_all(rt,y);
y=get_pre(rt,x);
}
while (1){
if (z.x == -Mod && z.y == -Mod) break;
r=get_nx(rt,z);
if (r.x==-Mod && r.y==-Mod || dcmp(slope(r,z) - slope(z,x)) < 0) break;
Del_all(rt,z);
z=get_nx(rt,x);
}
fhqtreap::Insert(rt,x);
}
int Query(int rt,double k){
using namespace fhqtreap;
int root=rt;
while (1){
double s1=tree[rt].lc? slope(tree[rt].val,get_pre(root,tree[rt].val)) : Mod;
double s2=tree[rt].rc? slope(tree[rt].val,get_nx(root,tree[rt].val)) : esp;
if (dcmp(k-s2) < 0) rt=tree[rt].rc;
else if (dcmp(k-s1) > 0) rt=tree[rt].lc;
else break;
}
return rt;
}
namespace fhqtreap{
inline void update(int rt){tree[rt].size=tree[tree[rt].lc].size+tree[tree[rt].rc].size+1;}
inline int add_node(Point x){
int rt=++tot;
tree[rt].val=x;
tree[rt].rank=rand()%Mod;
tree[rt].size=1;
return rt;
}
void spilt(int rt,int &rx,int &ry,Point val){
if (!rt){
rx=ry=0;
return;
}
if (tree[rt].val <= val) spilt(tree[rt].rc,tree[rx=rt].rc,ry,val);
else spilt(tree[rt].lc,rx,tree[ry=rt].lc,val);
update(rt);
}
void merge(int &rt,int rx,int ry){
if (!rx || !ry){
rt=rx+ry;
return;
}
if (tree[rx].rank <= tree[ry].rank) merge(tree[rt=rx].rc,tree[rx].rc,ry);
else merge(tree[rt=ry].lc,rx,tree[ry].lc);
update(rt);
}
void Insert(int &rt,Point x){
int new_node=add_node(x);
if (!rt){
rt=new_node;
return;
}
int rx,ry;
spilt(rt,rx,ry,x);
merge(rx,rx,new_node);
merge(rt,rx,ry);
}
void Del_all(int &rt,Point x){
int rx,ry,rz;
spilt(rt,rx,ry,x);
x.x-=esp*2;
spilt(rx,rx,rz,x);
merge(rt,rx,ry);
}
Point get_kth(int rt,int k){
int cnt=0;
while ((cnt=tree[tree[rt].lc].size+1)!=k){
if (k<cnt) rt=tree[rt].lc;
else k-=cnt,rt=tree[rt].rc;
}
return tree[rt].val;
}
Point get_pre(int &rt,Point x){
int rx,ry;
x.x-=esp*2;
spilt(rt,rx,ry,x);
if (!tree[rx].size){
rt=ry;
return Point(-Mod,-Mod);
}
Point k=get_kth(rx,tree[rx].size);
merge(rt,rx,ry);
return k;
}
Point get_nx(int &rt,Point x){
int rx,ry;
spilt(rt,rx,ry,x);
if (!tree[ry].size){
rt=rx;
return Point(-Mod,-Mod);
}
Point k=get_kth(ry,1);
merge(rt,rx,ry);
return k;
}
}
//dp[i]=max{dp[j]*a[j].rate*a[i].a/(a[j].rate*a[j].a+a[j].b)+d[j]*a[i].b/(a[j].rate*a[j].a+a[j].b)}