zoukankan      html  css  js  c++  java
  • [NOI2007]货币兑换 题解

    [NOI2007]货币兑换 题解

    Problem

    ​ 初始时有(m)的本金,已经知道之后(n)天内每一天(A)券与(B)券的价值,每一天可以买入或卖出金券。

    ​ 卖出金券方法如下:自己提供一个([0,100])内的实数(OP),意义为将(OP\%)(A)券和(OP\%)(B)券卖出。

    ​ 买入金券方法如下:每一天给定一个(R),自己给出一个买入的金额(IP),满足买入(A)券与(B)券数量之比为(R),总价值为(OP)

    ​ 求(n)天后拥有的最大总价值。

    Solution

    ​ 首先不难想到一个这样的策略,卖出金券时,一定全部卖出是最优的,这个感觉比较显然,有了这个,(OP)的限制就不存在了。

    ​ 考虑DP,我们可以得到一个这样的转移方程:

    (dp_i=max(dp_{i-1},maxlimits_{j=0}^{i-1}{A_i imes X_j+B_i imes Y_j})),其中(X_i=frac{R_i imes dp_i}{R_i imes A_i+B_i},Y_i=frac{dp_i}{R_i imes A_i+B_i})

    ​ 表示第(i)天不操作,或者把第(j)天买入的金券全部卖出的最大价值。这是一个(O(n^2))的算法,能拿到60pts。

    ​ 考虑如何去优化,看见式子中含与(i,j)有关部分之积(A_i imes dp_j),发现这是一个典型的斜率优化。

    ​ 忽略掉(max)符号,改写一下式子:(Y_j=-frac{A_i}{B_i}X_j+frac{dp_i}{B_i}),斜率已知,目标是最大化截距,可以维护一个上凸壳。

    ​ 但是,我们发现每一次加入的点((X_i,Y_i))无论在横坐标上还是纵坐标上都不单调,无法简单的用单调队列维护,于是考虑用平衡树动态维护凸壳,来实现在任意位置插入及删除,总复杂度(O(nlogn))

    Code

    #include<bits/stdc++.h>
    #define eps 1e-8
    #define DB double
    using namespace std;
    int n,m;
    DB dp[100005];
    DB A[100005],B[100005],R[100005],X[100005],Y[100005];
    
    struct Splay{
    
        int root;
    
        DB Lk[100005],Rk[100005];
    
        int f[100005],son[100005][2];
    
        inline int which(int x){
            return son[f[x]][1]==x;
        }
        
        inline DB slope(int a,int b){
            if(abs(X[a]-X[b])<eps)
                return -DBL_MAX;
            return (Y[a]-Y[b])/(X[a]-X[b]);
        }
        
        inline void rotate(int x,int &goal){
            int fx=f[x],gx=f[fx],d=which(x);
            if(fx==goal)
                goal=x;
            else
                son[gx][which(fx)]=x;
            son[fx][d]=son[x][d^1];son[x][d^1]=fx;
            f[son[fx][d]]=fx;f[fx]=x;f[x]=gx;
            return;
        }
    
        inline void splay(int x,int &goal){
            while(x!=goal){
                int fx=f[x];
                if(fx!=goal)
                    rotate(which(fx)==which(x)?fx:x,goal);
                rotate(x,goal);
            }
            return;
        }
    
        void insert(int x){
            if(!root){
                root=x;
                return;
            }
            int now=root,fx=0;
            while(now){
                fx=now;
                now=son[now][X[x]>X[now]+eps];
                if(!now){
                    f[x]=fx;
                    son[fx][X[x]>X[fx]+eps]=x;
                }
            }   
            return;
        }
    
        int findpre(int x){
            int now=son[x][0],ans=now;
            while(now){
                if(Lk[now]>=slope(x,now)+eps){
                    ans=now;
                    now=son[now][1];
                }
                else
                    now=son[now][0];
            }
            return ans;
        } 
    
        int findsuf(int x){
            int now=son[x][1],ans=now;
            while(now){
                if(Rk[now]+eps<=slope(x,now)){
                    ans=now;
                    now=son[now][0];
                }
                else
                    now=son[now][1];
            }
            return ans;
        }
    
        void Add(int x){
            splay(x,root);
            if(son[x][0]){
                int now=findpre(x);
                Lk[x]=Rk[now]=slope(x,now);
                splay(now,son[x][0]);
                son[now][1]=0;
            }   
            else
                Lk[x]=+DBL_MAX;
            if(son[x][1]){
                int now=findsuf(x);
                Rk[x]=Lk[now]=slope(x,now);
                splay(now,son[x][1]);
                son[now][0]=0;
            }
            else
                Rk[x]=-DBL_MAX;
            if(Lk[x]+eps<=Rk[x]){
                root=son[x][0];
                f[son[x][0]]=0;
                f[son[x][1]]=root;
                son[root][1]=son[x][1];
                Rk[root]=Lk[son[root][1]]=slope(root,son[root][1]);
            }
            return;
        }
    
        int find(DB k){
            int now=root;
            while(now){
                if(Rk[now]+eps<=k&&k+eps<=Lk[now])
                    return now;
                else if(k<Rk[now]+eps)
                    now=son[now][1];
                else if(k+eps>Lk[now])
                    now=son[now][0];
            }
            return 0;
        } 
    
    }T;
    
    int main(){
        
        scanf("%d",&n);
        scanf("%d",&m);
    
        for(register int i=1;i<=n;++i){
            scanf("%lf",&A[i]);
            scanf("%lf",&B[i]);
            scanf("%lf",&R[i]);
        }
    
        dp[0]=m;
        for(register int i=1;i<=n;++i){
            int j=T.find(-A[i]/B[i]);
            dp[i]=max(dp[i-1],A[i]*X[j]+B[i]*Y[j]);
            Y[i]=dp[i]/(R[i]*A[i]+B[i]);X[i]=Y[i]*R[i];
            T.insert(i);T.Add(i);
        }
    
        printf("%0.3lf
    ",dp[n]);
    
        return 0;
    }
    
  • 相关阅读:
    阿里云CentOS安装firefox闪退
    error: QApplication: No such file or directory
    CentOS下自动登陆root帐户
    Linux下常用软件
    【记录】haphost免费vps初始配置
    检测Linux VPS是Xen、OpenVZ还是KVM真假方法
    使用VNC远程管理VPS(Centos系统)
    配置suricata
    NGUI的输入框制作(attach- input filed script的使用)
    NGUI技能CD效果制作(sprite的type:filled)
  • 原文地址:https://www.cnblogs.com/zjy123456/p/13714833.html
Copyright © 2011-2022 走看看