NOI2007 currency exchange - CDQ partition optimization slope dp

Optimization of the slope dp maintain a convex hull. If \ (x, y \) coordinate is incremented, may be monotonous queue, if only \ (X \) is incremented, may be half the slope on the convex hull, if \ (x, y \) is not incremented, it is necessary in the projection package insert, or it may be a balanced tree partition cdq maintenance. However, I will not balance the tree, so I had to use a divide and conquer cdq.

topic

Per day given money exchange A, B exchange two kinds of gold certificates \ (A_i, B_i \) , and a fixed daily money for A, the ratio of two kinds of bills B \ ({A \ B over Rate_i} = \) , Sell be proportional to sell a, B two kinds of coupons each \ (OP \% \) , seeking \ (n \) maximum benefit days, since the amount of money for the \ (S \) .

\ (N \ leq 10 ^ 5 \)

dp

Title reminds us, every time to buy a certain finish spend all the money, sell some gold to sell all the tickets. (Since there is incomplete optimal solution Why use)

Thus, setting of \ (I \) maximum benefit for the day \ (F_i \) , in order to facilitate the introduction of the slope of the optimization equation set \ (F_i \) can be replaced by \ (x_i \) of securities, and A \ (y_i \ ) B-securities. Hereinafter, \ (Rate \) written \ (R & lt \) .
\ [x_i = R_i F_i {\ + B_i over A_iR_i} \]
\ [F_i = y_i. 1 {\ + B_i over A_iR_i} \]

There is equation
\ [f_i = \ max \ { f_ {i-1}, \ max \ {x_jA_i + y_jB_i \} \} \]

Do not consider (\-F_ {I}. 1) \ , in the form of a slope into the latter.

Proceeds to \ (F_i \) , \ (F_j \) than \ (F_k \) Excellent:
\ [x_jA_i + y_jB_i> x_kA_i + y_kB_i \]
\ [(x_j-x_k) A_i> - (y_j-Y_k) B_i \]
\ [{{y_j-y_k}
\ over {x_j-x_k}}> - {A_i \ over B_i} \] Note that to change the direction of the inequality symbol.

Obviously \ (x, y \) is not monotonic in nature, can not be directly built monotone convex hulls stack or queue.

cdq partition

Because cdq partition can be divided into two sections, and the statistics before a contribution to the piece, it is possible for the previous block \ (x \) be sorted, you can build a convex hull, the latter part of the transferred.

Note the order of three kinds:

  1. Partition before press \ (- {A_i \ over B_i } \) sorting to ensure that the second half of the convex hull queries are ordered so that scanning may be a pointer, or a two-division multiple log.
  2. Before divided into two, press \ (POS \) packet, \ (POS \ Leq MID \) placed before an, \ (POS> MID \) after a place, to ensure that a small number of large number of updates. Meanwhile, we can guarantee to the partition leaf is in numerical order for the leaf ,, i.e. \ (L = R & lt POS = \) , when the partition to the leaf node has been updated over all, so that can be processed \ (f_i = \ max \ {F_i,. 1-F_ {I} \} \) .
  3. Finally, press \ (X \) merge, convenient built convex convex hull.

Do not change the slope of the multiplication of forms, because more negative, change the multiplication should be noted that the direction of the inequality, the direct use of the slope more convenient.

Defined \ (slope (i, j) \) of \ (I \) to \ (J \) slope ( \ (I, J \) is the partition node number), to find the first convex hull \ (I \) satisfies \ (slope (S_i, S_ { i + 1}) <- {A_i \ over B_i} \) is the optimum transition point, note that if two \ (X \) are equal, it is a great slope You can not \ (eps \) judgment, sentence, etc. directly to ensure that the divisor is not \ (0 \) can be.

Process partition pseudo-code

  1. if (l == r) with f [i-1] to update f [i], calculates x, y exit
  2. cdq(l, mid)
  3. Monotone stack of [l, mid] convex hull construction
  4. Press \ (- {A_i \ over B_i } \) sequential scanning, transfer
  5. cdq(mid+1, r)
  6. Press x merge

Code

#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int N = 100005;
const double eps = 1e-9, inf = 1e9;
struct val{
    int p; double x, y;
}q[N], tmp[N];
double f[N], A[N], B[N], R[N];
int s[N];
bool cmp(val a, val b){return -(A[a.p] / B[a.p]) > -(A[b.p] / B[b.p]);}
double slope(int x, int y){
    if(q[x].x == q[y].x) return inf;
    return (q[y].y - q[x].y) / (q[y].x - q[x].x);
}

void cdq(int l, int r){
    if(l == r){ //完成对节点的处理
        f[l] = max(f[l], f[l-1]);
        q[l].x = f[l] / (A[l] * R[l] + B[l]) * R[l];
        q[l].y = f[l] / (A[l] * R[l] + B[l]);
        return ;
    }
    int mid = (l + r) >> 1, lp = l, rp = mid + 1, tp = l;
    for(int i=l; i<=r; i++) //分组
        if(q[i].p <= mid) tmp[lp++] = q[i];
        else tmp[rp++] = q[i];
    for(int i=l; i<=r; i++) q[i] = tmp[i];
    cdq(l, mid);
    int t = 0, p = 1;
    for(int i=l; i<=mid; i++){ //建凸壳
        while(t > 1 && slope(s[t], i) > slope(s[t-1], s[t])) --t;
        s[++t] = i;
    }
    for(int i=mid+1; i<=r; i++){ //转移
        while(p < t && slope(s[p], s[p+1]) > -A[q[i].p] / B[q[i].p]) ++p;
        f[q[i].p] = max(f[q[i].p], q[s[p]].x * A[q[i].p] + q[s[p]].y * B[q[i].p]);
    }
    cdq(mid+1, r);
    lp = l; rp = mid + 1; tp = l;
    while(lp <= mid && rp <= r) //归并
        if(q[lp].x < q[rp].x) tmp[tp++] = q[lp++];
        else tmp[tp++] = q[rp++];
    while(lp <= mid) tmp[tp++] = q[lp++];
    while(rp <= r) tmp[tp++] = q[rp++];
    for(int i=l; i<=r; i++) q[i] = tmp[i];
}

int main(){
    int n; double s;
    scanf("%d%lf", &n, &s);
    for(int i=1; i<=n; i++){
        scanf("%lf%lf%lf", &A[i], &B[i], &R[i]);
        q[i].p = i; //pos
        q[i].x = s / (A[q[i].p] * R[q[i].p] + B[q[i].p]) * R[q[i].p];
        q[i].y = s / (A[q[i].p] * R[q[i].p] + B[q[i].p]);
        f[i] = s; //用s初始化
    }
    sort(q+1, q+1+n, cmp);
    cdq(1, n);
    printf("%.3lf\n", f[n]);
    return 0;
}

Guess you like

Origin www.cnblogs.com/RiverHamster/p/cdq-slopedp.html