1. 程式人生 > >[NOI2007]貨幣兌換 「CDQ分治實現斜率優化」

[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 */
View Code