[BZOJ1492]货币兑换Cash

4 篇文章 0 订阅
3 篇文章 0 订阅

首先
推荐一篇博客:CDQ和整体二分
建议在学CDQ之前和学完CDQ之后都看一看,这个总结得十分的到位;
我当时就是刚学CDQ,网上题解看不懂,看了这篇博客后就自己YY出来了。

题目链接BZOJ1492

分析
1. 显然,有便宜尽量去占,没便宜一点也不碰,要买就买到没钱,要卖就卖光,才是最优解。
2. 设 dp[i] 为到第 i 天为止,最多能赚多少钱,X[i]为花光 dp[i] ,能买多少A券, Y[i] 为花光 dp[i] ,能买多少B券(两者是成给出的比例的);于是有一个显然的DP: dp[i]=max(dp[i1]X[j]×a[i]+Y[j]×b[i]) O(N2)
3. 考虑斜率优化,设 g[j,k]=(X[j]X[k])/(Y[j]Y[k]) ,维护凸壳,由于 Y 不单调,所以有两种方法:1. splay维护;2. CDQ分治。这里选择用CDQ分治。
4. 由于要维护g[j,k]<b[i]/a[i],于是按 b[i]/a[i] 从大到小排序日期;分治时先把日期从有序拆分成 [l..mid] [mid+1..r] 两部分,然后 slove(l,mid) ,再斜率优化用 dp[l..mid] 更新 dp[mid+1..r] ,最后归并排序 [l..r] 区间的日期(按 Y 权值排)。
5. 在用dp[l..mid]更新 dp[mid+1..r] 时,由于分治步骤,区间 [l..mid] Y 必定使排好序的,于是可以扫一遍建立凸壳;然后更新dp[mid+1..r]时维护 g[j,k]<b[i]/a[i] ,由于未 slove 的区间是按 b[i]/a[i] 排序的,所以 O(N) 扫一遍就好了。

上代码

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

typedef double DB;
const DB EPS = 1e-6;
const int N = 12e4 + 10;
const int INF = 0x3f3f3f3f;

int n;
DB R[N], A[N], B[N];
DB dp[N], X[N], Y[N];

int id[N];
inline bool cmpId(int a, int b) {
    return (-B[a] / A[a]) > (-B[b] / A[b]);
}
void init() {
    scanf("%d %lf", &n, &dp[1]);
    for (int i = 1; i <= n; i++)
        scanf("%lf %lf %lf", &A[i], &B[i], &R[i]), id[i] = i;
    sort(id + 1, id + n + 1, cmpId);
}

int ary[N];
void mergeSort(int l, int r) {
    int mid = (l + r) >> 1;
    int ll = l, rr = mid + 1, p = l;
    while (ll <= mid && rr <= r)
        if (Y[id[ll]] <= Y[id[rr]])
            ary[p++] = id[ll++];
        else ary[p++] = id[rr++];
    while (rr <= r) ary[p++] = id[rr++];
    while (ll <= mid) ary[p++] = id[ll++];
    for (int i = l; i <= r; i++) id[i] = ary[i];
}
void mergeResort(int l, int r) {
    int mid = (l + r) >> 1;
    int ll = l, rr = mid + 1;
    for (int i = l; i <= r; i++)
        if (id[i] <= mid)
            ary[ll++] = id[i];
        else ary[rr++] = id[i];
    for (int i = l; i <= r; i++) id[i] = ary[i];
}

struct Que {
    int head, tail, val[N];
    inline void clear() { head = tail = 0; }
    inline int size() { return tail - head; }
    inline void push(int a) { val[tail++] = a; }
    inline void popBack() { tail--; }
    inline void popFront() { head++; }
} Q;
inline DB calcK(int a, int b) {
    if (Y[a] == Y[b]) return INF;
    return (X[a] - X[b]) / (Y[a] - Y[b]);
}
inline bool judge(int a, int b, int c) {
//    if (Y[a] == Y[b] || Y[b] == Y[c]) return false;
    return calcK(a, b) > calcK(b, c) + EPS;
}
void figure(int l, int r) {
    if (l == r) {
        dp[l] = max(dp[l], dp[l - 1]);
        X[l] = R[l] / (R[l] * A[l] + B[l]) * dp[l];
        Y[l] = 1.0 / (R[l] * A[l] + B[l]) * dp[l];
        return;
    }
    int mid = (l + r) >> 1;
    mergeResort(l, r), figure(l, mid);
    for (int i = l; i <= mid; i++) {
        int now = id[i];
        while (Q.size() >= 2 && !judge(Q.val[Q.tail - 2], Q.val[Q.tail - 1], now))
            Q.popBack();
        Q.push(now);
    }
    for (int i = mid + 1; i <= r; i++) {
        int now = id[i];
        while (Q.size() >= 2 && calcK(Q.val[Q.head], Q.val[Q.head + 1]) >= (-B[now] / A[now]))
            Q.popFront();
        int tmp = Q.val[Q.head];
        dp[now] = max(dp[now], X[tmp] * A[now] + Y[tmp] * B[now]);
    }
    Q.clear(), figure(mid + 1, r), mergeSort(l, r);
}
int main() {
    init();
    figure(1, n);
    printf("%.3lf\n", dp[n]);
    return 0;
}

以上

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值