题目链接
终于补了这个坑了。。
先考虑 O ( n 2 ) O(n ^ 2) O(n2)咋整
显然可以得到式子
f
[
i
]
=
max
1
≤
j
<
i
{
f
[
j
]
∗
R
j
∗
A
i
+
B
i
R
j
∗
A
j
+
B
j
}
f[i] = \max_{1 \leq j < i} \left\{ f[j] * \frac{R_j * A_i + B_i}{R_j * A_j + B_j} \right\}
f[i]=1≤j<imax{f[j]∗Rj∗Aj+BjRj∗Ai+Bi}
自己瞎jb推一推吧
然后考虑这个式子有啥用
先把
m
a
x
max
max去了,然后把等式右边有关
i
i
i的给提出来
设
x
j
=
f
[
j
]
∗
R
j
R
j
∗
A
j
+
B
j
x_j = f[j] * \frac{R_j}{R_j * A_j + B_j}
xj=f[j]∗Rj∗Aj+BjRj,
y
j
=
f
[
j
]
∗
1
R
j
∗
A
j
+
B
j
y_j = f[j] * \frac{1}{R_j *A_j + B_j}
yj=f[j]∗Rj∗Aj+Bj1
显然可以得到
f
[
i
]
=
x
j
∗
A
i
+
y
j
∗
B
i
f[i] = x_j * A_i + y_j * B_i
f[i]=xj∗Ai+yj∗Bi
变换得
y
j
=
−
A
i
B
i
x
j
+
f
[
i
]
B
i
y_j = -\frac{A_i}{Bi} x_j + \frac{f[i]}{Bi}
yj=−BiAixj+Bif[i]
考虑几何意义:事实上上式就是一个斜率为
−
A
i
B
i
-\frac{A_i}{Bi}
−BiAi的,过点
(
x
j
,
y
j
)
(x_j,y_j)
(xj,yj)的直线,而
f
[
i
]
f[i]
f[i]即为这条直线的截距,
那么事实上,我们每次做
d
p
dp
dp的时候,所有决策都是这样的点,然后我们就拿一条直线一直切,看切到哪个点的时候截距最大,那么我们的
f
f
f值就取这个截距
进而发现,我们每次切的点,都在当前决策点所形成的凸包上
那么我们就有一个大胆的想法:用
s
p
l
a
y
splay
splay维护一个凸包,然后插入插入,查询就在上面直接二分啥的就完事了
这是一个做法,当然还有一个做法:
c
d
q
cdq
cdq分治
考虑用左边的操作去更新右边的询问(每条直线可以看做是询问
先把询问按斜率排个序,然后
c
d
q
cdq
cdq的每一层考虑把左边的点按
x
x
x坐标
y
y
y坐标双关键字排排序,
然后用左边的点暴力构造一个上凸壳,最后把右边的询问更新就好了
更新操作大概是,考虑当前斜率已经从小到大排好,那么假设一个点
i
i
i在上凸壳上切到的点是
j
j
j,那么点
i
+
1
i + 1
i+1切到的点一定小等
j
j
j(这个可以画画图看下是为啥,大概是类似线性规划的**操作
那么我们就直接一边更新,一边弹栈就好了
P S . PS. PS.把左边的点排序并不用真的排序,只需要像归并一样做上来就好了
代码:
#include<cstdio>
#include<cmath>
#include<queue>
#include<stack>
#include<vector>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long LL;
typedef double DL;
const int maxn = 100010;
const DL INF = 1e100;
const DL eps = 1e-7;
struct point{
DL x,y;
}p[maxn],Tmp[maxn],stk[maxn];
int n,top,tmp[maxn],q[maxn];
DL a[maxn],b[maxn],R[maxn],f[maxn];
inline LL getint()
{
LL ret = 0,f = 1;
char c = getchar();
while (c < '0' || c > '9')
{
if (c == '-') f = -1;
c = getchar();
}
while (c >= '0' && c <= '9')
ret = ret * 10 + c - '0',c = getchar();
return ret * f;
}
inline DL calc(point p,int i)
{
return p.x * a[i] + p.y * b[i];
}
inline DL getk(point u,point v)
{
if (fabs(u.x - v.x) <= eps) return v.y > u.y ? INF : -INF;
return (u.y - v.y) / (u.x - v.x);
}
inline void solve(int l,int r)
{
if (l == r)
{
f[l] = max(f[l],f[l - 1]);
p[l].x = f[l] * R[l] / (R[l] * a[l] + b[l]);
p[l].y = f[l] / (R[l] * a[l] + b[l]);
return;
}
int mid = l + r >> 1;
int L = l - 1,R = mid;
for (int i = l; i <= r; i++)
if (q[i] <= mid) tmp[++L] = q[i]; else tmp[++R] = q[i];
for (int i = l; i <= r; i++) q[i] = tmp[i];
solve(l,mid); top = 0;
for (int i = l; i <= mid; i++)
{
while (top >= 2 && getk(stk[top - 1],stk[top]) < getk(stk[top - 1],p[i])) top--;
stk[++top] = p[i];
}
for (int i = mid + 1; i <= r; i++)
{
while (top >= 2 && calc(stk[top - 1],q[i]) >= calc(stk[top],q[i])) top--;
f[q[i]] = max(f[q[i]],calc(stk[top],q[i]));
}
solve(mid + 1,r);
int now = l - 1,i = l,j = mid + 1;
while (i <= mid && j <= r)
{
if (p[i].x < p[j].x || (fabs(p[i].x - p[j].x) <= eps && p[i].y < p[j].y)) Tmp[++now] = p[i++];
else Tmp[++now] = p[j++];
}
while (i <= mid) Tmp[++now] = p[i++]; while (j <= r) Tmp[++now] = p[j++];
for (int i = l; i <= r; i++) p[i] = Tmp[i];
}
inline int cmp(int x,int y)
{
return -a[x] / b[x] < -a[y] / b[y];
}
int main()
{
#ifdef AMC
freopen("AMC1.txt","r",stdin);
#endif
n = getint(); f[1] = getint();
for (int i = 1; i <= n; i++)
scanf("%lf%lf%lf",&a[i],&b[i],&R[i]);
for (int i = 1; i <= n; i++) q[i] = i;
sort(q + 1,q + n + 1,cmp); solve(1,n);
printf("%.3lf",f[n]);
return 0;
}