于是分别设
f
i
,
x
i
,
y
i
f_i,x_i,y_i
fi,xi,yi 表示到第
i
i
i 天时的最大收益、最多能购买的
A
A
A 劵数量、最多能购买的
B
B
B 劵数量,则转移为:
f
i
=
max
j
=
1
i
−
1
{
f
i
−
1
,
A
i
×
x
j
+
B
i
×
y
j
}
f_i = \max\limits^{i - 1}_{j = 1}\{f_{i - 1}, A_i \times x_j + B_i \times y_j\}
fi=j=1maxi−1{fi−1,Ai×xj+Bi×yj}
y
i
=
f
i
A
i
×
R
a
t
e
i
+
B
i
y_i = \frac{f_i}{A_i \times Rate_i + B_i}
yi=Ai×Ratei+Bifi
x
i
=
R
a
t
e
i
×
y
i
x_i = Rate_i \times y_i
xi=Ratei×yi
时间复杂度
O
(
N
2
)
O(N^2)
O(N2),不能通过。
x
i
,
y
i
x_i, y_i
xi,yi 只由
f
i
f_i
fi 确定,考虑怎样优化
f
i
f_i
fi 的转移。
先不管转移中的
f
i
−
1
f_{i - 1}
fi−1 和
m
a
x
max
max,则有:
f
i
=
A
i
×
x
j
+
B
i
×
y
j
f_i = A_i \times x_j + B_i \times y_j
fi=Ai×xj+Bi×yj
选择两个含
j
j
j 的不同项,分别移到等式两边,并使右边含
j
j
j 的项系数为1,把只含
i
i
i 的项移到左边:
−
A
i
B
i
×
x
j
+
f
i
B
i
=
y
j
- \frac{A_i}{B_i} \times x_j + \frac{f_i}{B_i} = y_j
−BiAi×xj+Bifi=yj
因为要使
f
i
f_i
fi 最大,即为让一条斜率为
−
A
i
B
i
-\frac{A_i}{B_i}
−BiAi 的直线经过点
(
x
j
,
y
j
)
(x_j, y_j)
(xj,yj),使得它的截距(
f
i
B
i
\frac{f_i}{B_i}
Bifi)最大。
通过画图我们可以发现,最优解必然在点
(
x
j
,
y
j
)
(x_j, y_j)
(xj,yj) 构成的上凸壳上(如果取最小就是下凸壳),并且最优解的
j
j
j 即满足它与它在凸壳上的上一个点所在直线的斜率大于
−
A
i
B
i
-\frac{A_i}{B_i}
−BiAi,它与它在凸壳上的下一个点所在直线的斜率小于
−
A
i
B
i
-\frac{A_i}{B_i}
−BiAi。
然而这题
x
j
,
y
j
x_j, y_j
xj,yj 都不递增,不能用单调队列或者二分来维护。
S
p
l
a
y
Splay
Splay 大法好!
按
x
j
x_j
xj 的大小在
S
p
l
a
y
Splay
Splay 上排好顺序,记录
l
k
i
,
r
k
i
lk_i, rk_i
lki,rki分别表示与凸壳上一个点和下一个点所在直线的斜率。
查找最优解只要在
S
p
l
a
y
Splay
Splay 上走,主要要解决的是如何动态插入一个点
x
x
x。
同样考虑二分,能作为
x
x
x 在凸壳上的上一个点
y
y
y 要满足这两点所在直线的斜率小于
l
k
y
lk_y
lky,并且
y
y
y 要取到最大,
x
x
x 在凸壳上的下一个点同理。
若插入后
l
k
x
<
r
k
x
lk_x < rk_x
lkx<rkx,点
x
x
x 在凸壳内,直接把点
x
x
x 删除。
注意精度问题,时间复杂度
O
(
均
摊
n
log
n
)
O(均摊 n \log n)
O(均摊nlogn)。
Code
数据中似乎不用考虑点在凸壳内的情况。
两个地方把 x 写成 y 还有 90pts。
这数据到底有多水……
#include<iostream>#include<cstdio>#include<cctype>#include<algorithm>#include<cstring>#include<cstdlib>usingnamespace std;constdouble eps =1e-9;constdouble feps =-1e-9;constdouble inf =1e15;constdouble finf =-1e15;constint N =1e5+5;double lk[N], rk[N], f[N], vx[N], vy[N];int n, T, rt, lc[N], rc[N], fa[N];inlinedoubleMax(double x,double y){return x + eps >= y ? x : y;}inlineboolWhich(int x){return lc[fa[x]]== x;}inlinevoidRotate(int x){int y = fa[x], z = fa[y],
b = lc[y]== x ? rc[x]: lc[x];if(z)(lc[z]== y ? lc[z]: rc[z])= x;
fa[x]= z; fa[y]= x;if(b) fa[b]= y;if(lc[y]== x) rc[x]= y, lc[y]= b;else lc[x]= y, rc[y]= b;}inlinevoidSplay(int x,int tar){while(fa[x]!= tar){if(fa[fa[x]]!= tar)Which(fa[x])==Which(x)?Rotate(fa[x]):Rotate(x);Rotate(x);}if(!tar) rt = x;}inlinedoubleSlope(int x,int y){if(vx[x]- vx[y]<= eps && vx[x]- vx[y]>= feps)return finf;return(vy[y]- vy[x])/(vx[y]- vx[x]);}inlineintfindLeft(int x,int y){int res = x;while(x){if(lk[x]+ eps >=Slope(x, y)) res = x, x = rc[x];else x = lc[x];}return res;}inlineintfindRight(int x,int y){int res = x;while(x){if(rk[x]<=Slope(y, x)+ eps) res = x, x = lc[x];else x = rc[x];}return res;}inlinevoidInsert(){int x = rt, y =0, dir;++T;while(x){
y = x;if(vx[T]<= vx[x]+ eps) x = lc[x], dir =0;else x = rc[x], dir =1;}
fa[x = T]= y;if(y)(dir ? rc[y]: lc[y])= x;Splay(x,0);if(lc[x]){int z =findLeft(lc[x], x);Splay(z, x); fa[rc[z]]= rc[z]=0;
rk[z]= lk[x]=Slope(z, x);}if(rc[x]){int z =findRight(rc[x], x);Splay(z, x); fa[lc[z]]= lc[z]=0;
lk[z]= rk[x]=Slope(x, z);}if(lk[x]<= rk[x]+ eps){
rt = lc[x]; rc[rt]= rc[x];
fa[rc[x]]= rt; fa[rt]=0;
lc[x]= rc[x]= fa[x]=0;
rk[rt]= lk[rc[rt]]=Slope(rt, rc[rt]);}}inlineintQuery(double v){int x = rt, res =0;while(x){if(lk[x]+ eps >= v) res = x, x = rc[x];else x = lc[x];}return res;}intmain(){scanf("%d%lf",&n,&f[0]);double a, b, r;for(int i =1; i <= n;++i)
lk[i]= inf, rk[i]= finf;for(int i =1; i <= n;++i){scanf("%lf%lf%lf",&a,&b,&r);int j =Query(-a / b);
f[i]=Max(f[i -1], a * vx[j]+ b * vy[j]);
vy[i]= f[i]/(a * r + b);
vx[i]= vy[i]* r;Insert();}printf("%.3lf\n", f[n]);}