首先每次买卖一定是在某天 $k$ 以当时的最大收入买入,再到第 $i$ 天卖出,那么易得方程:

$$f_i = \max \{\frac{A_iRate_kf_k}{A_kRate_k + B_k} + \frac{B_if_k}{A_kRate_k + B_k}\}$$

再令

$$\left\{\begin{aligned} x_k = \frac{Rate_kf_k}{A_kRate_k + B_k} \\ y_k = \frac{f_k}{A_kRate_k + B_k}\end{aligned}\right.$$

则有

$$\begin{aligned} f_i &= \max \{A_ix_k + B_iy_k\} \\ y_k &= - \frac{A_i}{B_i}x_k + \frac{f_i}{B_i} \end{aligned}$$

那么现在需要找到一个点 $(x_k, y_k)$ 使得直线的截距最大

由于斜率和横坐标皆不满足单调性,可以用平衡树等维护,这里使用CDQ分治实现

实现过程如下:

Ⅰ 将数据按照斜率$\frac{A_i}{B_i}$降序排序

Ⅱ 将区间按照操作顺序分为左右两部分处理

Ⅲ 先处理左半部分,维护左半边凸包(注意,此时左半边已按照 $x$ 排序)

Ⅳ 处理左半边对右半边的影响,由于已按照斜率降序排序,所以普通斜率优化即可

Ⅴ 将区间按照 $x$ 排序

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>

using namespace std;

const int MAXN = 1e05 + 10;

const double INF = 1e60;
const double eps = 1e-08;

int Dcmp (double p) {
if (fabs (p) < eps)
return 0;
return p < 0 ? - 1 : 1;
}

struct CashSt {
double a, b, rate;
double k, x, y;
int index;

CashSt () {}

bool operator < (const CashSt& p) const {
return Dcmp (x - p.x) == 0 ? Dcmp (y - p.y) < 0 : Dcmp (x - p.x) < 0;
}
} ;
CashSt Cash[MAXN];
bool comp (const CashSt& a, const CashSt& b) {
return Dcmp (a.k - b.k) > 0;
}

int N;

double slope (CashSt a, CashSt b) {
if (Dcmp (b.x - a.x) == 0)
return INF;
return (b.y - a.y) / (b.x - a.x);
}
double f[MAXN]= {0};
CashSt Que[MAXN];
int l = 1, r = 0;
CashSt temp[MAXN];
void CDQ (int left, int right) {
if (left == right) {
f[left] = max (f[left], f[left - 1]);
Cash[left].y = f[left] / (Cash[left].a * Cash[left].rate + Cash[left].b);
Cash[left].x = Cash[left].y * Cash[left].rate;
return ;
}
int mid = (left + right) >> 1;
int p1 = left - 1, p2 = mid;
for (int i = left; i <= right; i ++)
Cash[i].index <= mid ? temp[++ p1] = Cash[i] : temp[++ p2] = Cash[i];
for (int i = left; i <= right; i ++)
Cash[i] = temp[i];
CDQ (left, mid);
l = 1, r = 0;
for (int i = left; i <= mid; i ++) {
while (l < r && Dcmp (slope (Que[r - 1], Que[r]) - slope (Que[r], Cash[i])) < 0)
r --;
Que[++ r] = Cash[i];
}
for (int i = mid + 1; i <= right; i ++) {
while (l < r && Dcmp (slope (Que[l], Que[l + 1]) - Cash[i].k) > 0)
l ++;
f[Cash[i].index] = max (f[Cash[i].index], Cash[i].a * Que[l].x + Cash[i].b * Que[l].y);
}
CDQ (mid + 1, right);
l = left, r = mid + 1;
int p = 0;
while (l <= mid && r <= right) {
// if (Dcmp (Cash[l].x - Cash[r].x) < 0 || (Dcmp (Cash[l].x - Cash[r].x) == 0 && Dcmp (Cash[l].y - Cash[r].y) < 0))
if (Cash[l] < Cash[r])
temp[++ p] = Cash[l], l ++;
else
temp[++ p] = Cash[r], r ++;
}
while (l <= mid)
temp[++ p] = Cash[l], l ++;
while (r <= right)
temp[++ p] = Cash[r], r ++;
for (int i = 1; i <= p; i ++)
Cash[i + left - 1] = temp[i];
}

double getnum () {
double num = 0.0;
char ch = getchar ();
double T = 1.0;

while (! isdigit (ch))
ch = getchar ();
while (isdigit (ch))
num = num * 10.0 + (ch - '0') * 1.0, ch = getchar ();
if (ch == '.') {
ch = getchar ();
while (isdigit (ch))
num = num + (T /= 10.0) * (ch - '0'), ch = getchar ();
}

return num;
}

int main () {
// freopen ("Input.txt", "r", stdin);

scanf ("%d%lf", & N, & f[0]);
for (int i = 1; i <= N; i ++) {
double a = getnum (), b = getnum (), rate = getnum ();
Cash[i].a = a, Cash[i].b = b, Cash[i].rate = rate;
Cash[i].index = i;
Cash[i].k = - a / b;
}
sort (Cash + 1, Cash + N + 1, comp);
CDQ (1, N);
printf ("%.3f\n", f[N]);

return 0;
}

/*
3 100
1 1 1
1 2 2
2 2 3
*/