[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

 

posted @ 2018-12-20 16:58  Colythme  阅读(201)  评论(0编辑  收藏  举报