zoukankan      html  css  js  c++  java
  • 洛谷 P4027 [NOI2007] 货币兑换

    小 Y 最近在一家金券交易所工作。该金券交易所只发行交易两种金券:A 纪念券(以下简称 A 券)和 B 纪念券(以下简称 B 券)。每个持有金券的顾客都有一个自己的帐户。金券的数目可以是一个实数。

    每天随着市场的起伏波动,两种金券都有自己当时的价值,即每一单位金券当天可以兑换的人民币数目。我们记录第 (K) 天中 A 券和 B 券的价值分别为 (A_K)(B_K) (元/单位金券)。

    为了方便顾客,金券交易所提供了一种非常方便的交易方式:比例交易法。

    比例交易法分为两个方面:

    a) 卖出金券:顾客提供一个 ([0,100]) 内的实数 (OP) 作为卖出比例,其意义为:将 (OP\%) 的 A 券和 (OP\%) 的 B 券以当时的价值兑换为人民币;

    b) 买入金券:顾客支付 (IP) 元人民币,交易所将会兑换给用户总价值为 (IP) 的金券,并且,满足提供给顾客的 A 券和 B 券的比例在第 (K) 天恰好为 (mathrm{Rate}_ K)

    例如,假定接下来 (3) 天内的 (A_K,B_K,mathrm{Rate}_ K) 的变化分别为:

    时间 (A_K) (B_K) (mathrm{Rate}_ K)
    第一天 (1) (1) (1)
    第二天 (1) (2) (2)
    第三天 (2) (2) (3)

    假定在第一天时,用户手中有 (100) 元人民币但是没有任何金券。

    用户可以执行以下的操作:

    时间 用户操作 人民币(元) A 券的数量 B 券的数量
    开户 (100) (0) (0)
    第一天 买入 (100) (0) (50) (50)
    第二天 卖出 (50\%) (75) (25) (25)
    第二天 买入 (60) (15) (55) (40)
    第三天 卖出 (100\%) (205) (0) (0)

    注意到,同一天内可以进行多次操作。

    小 Y 是一个很有经济头脑的员工,通过较长时间的运作和行情测算,他已经知道了未来 (N) 天内的 A 券和 B 券的价值以及 (mathrm{Rate})。他还希望能够计算出来,如果开始时拥有 (S) 元钱,那么 (N) 天后最多能够获得多少元钱。

    (N le 10^5)


    注意到在一天内全买和全买一定是最优。

    考虑先直接dp,设 (f_i) 表示第 (i) 天能获得的最多的钱,那么枚举在一天买了金券一直到现在才卖,就有:

    [f_i=f_{i-1} ]

    [f_i=max_{j=1}^{i-1}frac{f_ja_irate_j}{b_j+rate_ja_j}+frac{f_jb_i}{b_j+rate_ja_j} ]

    考虑如何优化,设 (c_i=b_i+rate_ia_i) ,可以把dp式写成:

    [f_i=max_{j=1}^{i-1}frac{f_jrate_j}{c_j}a_i+frac{f_j}{c_j}b_i ]

    (b_i) 提出来可以得到:

    [f_i=max_{j=1}^{i-1}b_i(frac{f_jrate_j}{c_j}frac{a_i}{b_i}+frac{f_j}{c_j}) ]

    可以发现这是一次函数的形式,那么我们现在需要支持两个操作:

    1. 插入一条直线。
    2. 查询 (x=t) 时所有函数的最大值。

    可以李超树直接做,也可以splay维护凸包(我不会),我们考虑如何cdq分治来完成。

    而因为斜率和 (t) 都不是单调的,我们可以先把 (t) 变成单调的,然后归并斜率,用单调栈维护凸包来更新答案。

    具体来说,我们对所有 (t) 排完序之后在分治开头把这个有序的区间分给左右区间,然后分治左区间,得到左区间斜率递增的直线,插到单调栈中,插入方法是这样的:

    对于两条直线 (y_1=k_1x+b_1,y_2=k_2x+b_2) ,如果 (y_1<y_2)(k_1>k_2) ,那么可以得到 (x<frac{b_2-b_1}{k_1-k_2}) ,在单调栈中我们保持相邻的这个值单调递增,然后双指针更新右区间的答案就可以了,分治完右区间再把直线归并成斜率递增。

    Code

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    const int N = 1e5;
    const double eps = 1e-8;
    using namespace std;
    int n,s,top;
    double f[N + 5],a[N + 5],b[N + 5],rate[N + 5],c[N + 5];
    struct node
    {
        double x;
        int id;
    }d[N + 5],tmp[N + 5];
    struct line
    {
        double k,b;
    }q[N + 5],stk[N + 5],nq[N + 5];
    double key(line x,line y)
    {
        if (x.k == y.k)
        {
            if (y.b < x.b)
                return eps;
            else
                return -eps;
        }
        return (y.b - x.b) / (x.k - y.k);
    }
    void add(line l)
    {
        while (top > 1 && key(stk[top - 1],stk[top]) > key(stk[top],l))
            top--;
        stk[++top] = l;
    }
    void cdq(int l,int r)
    {
        if (l == r)
        {
            f[l] = max(f[l],f[l - 1]);
            q[l] = (line){f[l] * rate[l] / c[l],f[l] / c[l]};
            return;
        }
        int mid = l + r >> 1,ll = l,rr = mid + 1;
        for (int i = l;i <= r;i++)
            if (d[i].id <= mid)
                tmp[ll++] = d[i];
            else
                tmp[rr++] = d[i];
        for (int i = l;i <= r;i++)
            d[i] = tmp[i];
        cdq(l,mid);
        top = 0;
        for (int i = l;i <= mid;i++)
            add(q[i]);
        ll = 1;
        for (int i = mid + 1;i <= r;i++)
        {
            while (ll < top && key(stk[ll],stk[ll + 1]) < d[i].x)
                ll++;
            f[d[i].id] = max(f[d[i].id],b[d[i].id] * (d[i].x * stk[ll].k + stk[ll].b));
        }
        cdq(mid + 1,r);
        ll = l;
        int cnt = 0;
        for (int i = mid + 1;i <= r;i++)
        {
            while (ll <= mid && q[ll].k < q[i].k)
                nq[++cnt] = q[ll++];
            nq[++cnt] = q[i];
        }
        for (int i = ll;i <= mid;i++)
            nq[++cnt] = q[i];
        for (int i = l;i <= r;i++)
            q[i] = nq[i - l + 1];
    }
    bool cmp(node a,node b)
    {
        return a.x < b.x;
    }
    int main()
    {
        scanf("%d%d",&n,&s);
        for (int i = 1;i <= n;i++)
        {
            scanf("%lf%lf%lf",&a[i],&b[i],&rate[i]);
            c[i] = b[i] + rate[i] * a[i];
            d[i] = (node){a[i] / b[i],i};
        }
        sort(d + 1,d + n + 1,cmp);
        f[1] = s;
        cdq(1,n);
        printf("%.3lf
    ",f[n]);
        return 0;
    }
    
  • 相关阅读:
    94. Binary Tree Inorder Traversal
    101. Symmetric Tree
    38. Count and Say
    28. Implement strStr()
    实训团队心得(1)
    探索性测试入门
    LC.278. First Bad Version
    Search in Unknown Sized Sorted Array
    LC.88. Merge Sorted Array
    LC.283.Move Zeroes
  • 原文地址:https://www.cnblogs.com/sdlang/p/14465299.html
Copyright © 2011-2022 走看看