[NOI2007]货币兑换 「CDQ分治实现斜率优化」
首先每次买卖一定是在某天 $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 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <cmath> 6 7 using namespace std; 8 9 const int MAXN = 1e05 + 10; 10 11 const double INF = 1e60; 12 const double eps = 1e-08; 13 14 int Dcmp (double p) { 15 if (fabs (p) < eps) 16 return 0; 17 return p < 0 ? - 1 : 1; 18 } 19 20 struct CashSt { 21 double a, b, rate; 22 double k, x, y; 23 int index; 24 25 CashSt () {} 26 27 bool operator < (const CashSt& p) const { 28 return Dcmp (x - p.x) == 0 ? Dcmp (y - p.y) < 0 : Dcmp (x - p.x) < 0; 29 } 30 } ; 31 CashSt Cash[MAXN]; 32 bool comp (const CashSt& a, const CashSt& b) { 33 return Dcmp (a.k - b.k) > 0; 34 } 35 36 int N; 37 38 double slope (CashSt a, CashSt b) { 39 if (Dcmp (b.x - a.x) == 0) 40 return INF; 41 return (b.y - a.y) / (b.x - a.x); 42 } 43 double f[MAXN]= {0}; 44 CashSt Que[MAXN]; 45 int l = 1, r = 0; 46 CashSt temp[MAXN]; 47 void CDQ (int left, int right) { 48 if (left == right) { 49 f[left] = max (f[left], f[left - 1]); 50 Cash[left].y = f[left] / (Cash[left].a * Cash[left].rate + Cash[left].b); 51 Cash[left].x = Cash[left].y * Cash[left].rate; 52 return ; 53 } 54 int mid = (left + right) >> 1; 55 int p1 = left - 1, p2 = mid; 56 for (int i = left; i <= right; i ++) 57 Cash[i].index <= mid ? temp[++ p1] = Cash[i] : temp[++ p2] = Cash[i]; 58 for (int i = left; i <= right; i ++) 59 Cash[i] = temp[i]; 60 CDQ (left, mid); 61 l = 1, r = 0; 62 for (int i = left; i <= mid; i ++) { 63 while (l < r && Dcmp (slope (Que[r - 1], Que[r]) - slope (Que[r], Cash[i])) < 0) 64 r --; 65 Que[++ r] = Cash[i]; 66 } 67 for (int i = mid + 1; i <= right; i ++) { 68 while (l < r && Dcmp (slope (Que[l], Que[l + 1]) - Cash[i].k) > 0) 69 l ++; 70 f[Cash[i].index] = max (f[Cash[i].index], Cash[i].a * Que[l].x + Cash[i].b * Que[l].y); 71 } 72 CDQ (mid + 1, right); 73 l = left, r = mid + 1; 74 int p = 0; 75 while (l <= mid && r <= right) { 76 // 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)) 77 if (Cash[l] < Cash[r]) 78 temp[++ p] = Cash[l], l ++; 79 else 80 temp[++ p] = Cash[r], r ++; 81 } 82 while (l <= mid) 83 temp[++ p] = Cash[l], l ++; 84 while (r <= right) 85 temp[++ p] = Cash[r], r ++; 86 for (int i = 1; i <= p; i ++) 87 Cash[i + left - 1] = temp[i]; 88 } 89 90 double getnum () { 91 double num = 0.0; 92 char ch = getchar (); 93 double T = 1.0; 94 95 while (! isdigit (ch)) 96 ch = getchar (); 97 while (isdigit (ch)) 98 num = num * 10.0 + (ch - '0') * 1.0, ch = getchar (); 99 if (ch == '.') { 100 ch = getchar (); 101 while (isdigit (ch)) 102 num = num + (T /= 10.0) * (ch - '0'), ch = getchar (); 103 } 104 105 return num; 106 } 107 108 int main () { 109 // freopen ("Input.txt", "r", stdin); 110 111 scanf ("%d%lf", & N, & f[0]); 112 for (int i = 1; i <= N; i ++) { 113 double a = getnum (), b = getnum (), rate = getnum (); 114 Cash[i].a = a, Cash[i].b = b, Cash[i].rate = rate; 115 Cash[i].index = i; 116 Cash[i].k = - a / b; 117 } 118 sort (Cash + 1, Cash + N + 1, comp); 119 CDQ (1, N); 120 printf ("%.3f\n", f[N]); 121 122 return 0; 123 } 124 125 /* 126 3 100 127 1 1 1 128 1 2 2 129 2 2 3 130 */