zoukankan      html  css  js  c++  java
  • [bzoj1492][NOI2007]货币兑换Cash

    来自FallDream的博客,未经允许,请勿转载,谢谢。

    题目好难解释啊,大家自己看原题吧??
    小Y最近在一家金券交易所工作。该金券交易所只发行交易两种金券:A纪念券(以下简称A券)和 B纪念券(以下简称B券)。每个持有金券的顾客都有一个自己的帐户。金券的数目可以是一个实数。每天随着市场的起伏波动,两种金券都有自己当时的价值,即每一单位金券当天可以兑换的人民币数目。我们记录第 K 天中 A券 和 B券 的价值分别为 AK 和 BK(元/单位金券)。为了方便顾客,金券交易所提供了一种非常方便的交易方式:比例交易法。比例交易法分为两个方面:(a)卖出金券:顾客提供一个 [0,100] 内的实数 OP 作为卖出比例,其意义为:将 OP% 的 A券和 OP% 的 B券 以当时的价值兑换为人民币;(b)买入金券:顾客支付 IP 元人民币,交易所将会兑换给用户总价值为 IP 的金券,并且,满足提供给顾客的A券和B券的比例在第 K 天恰好为 RateK;例如,假定接下来 3 天内的 Ak、Bk、RateK 的变化分别为:
    假定在第一天时,用户手中有 100元 人民币但是没有任何金券。用户可以执行以下的操作:
    注意到,同一天内可以进行多次操作。小Y是一个很有经济头脑的员工,通过较长时间的运作和行情测算,他已经知道了未来N天内的A券和B券的价值以及Rate。他还希望能够计算出来,如果开始时拥有S元钱,那么N天后最多能够获得多少元钱。
    【提示】
    必然存在一种最优的买卖方案满足:每次买进操作使用完所有的人民币;每次卖出操作卖出所有的金券。
    n<=100000,0<AK≤10;0<BK≤10;0<RateK≤100;MaxProfit≤10^9。
     
    题解:首先这个提示已经告诉了你这是一个dp啦。用f[i]表示前i天最多能获得的钱,那么假设这一天全部买进,那么获得的金券也就确定了。
    n^2dp很simple,假设x[i],y[i]分别表示第i天最多买进的ab两种金券的数量,那么f[i]=max(f[i-1],x[j]*a[i]+y[j]*b[i])
    考虑斜率优化,f[i]=max(x[j]*a[i]+y[j]*b[i]),y[j]=(-a[i]/b[i])*x[j]+f[i]/b[i],-a[i]/b[i]看作斜率,表示成了关于平面x,y上的斜率关系。bi不变,要让fi最大,就要让截距最大。
    很容易发现,满足的点都在(x,y)的上凸壳上,所以我们维护这个凸壳。
     
    怎么维护凸壳呢?下面介绍两种方法。
    1)splay
    由于xi不一定是连续的,所以不能用单调队列,所以用splay把所有点维护起来,每个点记录和左右两边的点的斜率。
    每次查询,我们用一条(-ai/bi)的直线去切割这个凸壳,就可以找到最优的转移点。
    每次插入,我们让按x坐标插入,然后往两边找到它能作为凸壳时连接的点,删去中间的点。假如找不到,也就说明它在凸壳内,把它自己删掉就行了。
    这样我们就能在O(nlogn)的时间内解决该问题。
    2)cdq分治
    但是我们还有更简单的办法,那就是cdq分治。我发现我对于每个i做的事情,都是用它的斜率在前面找一个点来更新他,而复杂点在于x的无序,不能用单调队列维护。如果我能让x和查询的斜率变成有序的,我就可以用单调队列快速完成。很自然想到cdq分治,我保证每一段的斜率有序,在分治结束后,把它的坐标再归并起来,这样在对一段区间"治"的时候,左边的点有序,可以直接算出凸壳,右边的询问也有序,可以直接查询。
    这样更新一段区间的复杂度是O(n)的,最多log层,加上初始的排序也是nlogn,总复杂度nlogn。
    跑起来会比splay慢一些些,但是代码复杂度方面,稳胜splay
     
    附上两种方法的代码 
    splay维护凸壳:
    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #define eps 1e-8
    #define MN 100000
    #define INF 2000000000
    using namespace std;
    inline int read()
    {
        int x = 0 , f = 1; char ch = getchar();
        while(ch < '0' || ch > '9'){ if(ch == '-') f = -1;  ch = getchar();}
        while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
        return x * f;
    }
    
    int fa[MN+5],c[MN+5][2],n,rt=0;
    double a[MN+5],b[MN+5],x[MN+5],y[MN+5],r[MN+5],L[MN+5],R[MN+5],f[MN+5];
    
    inline double getslop(int j,int k)
    {
        if(fabs(x[j]-x[k])<eps)return -INF;
        return (y[j]-y[k])/(x[j]-x[k]);
    }
    
    void rotate(int x,int&k)
    {
        int y=fa[x],z=fa[y],l=c[y][1]==x,r=l^1;
        if(y!=k) c[z][c[z][1]==y]=x;else k=x;
        fa[x]=z;fa[y]=x;fa[c[x][r]]=y;
        c[y][l]=c[x][r];c[x][r]=y;
    }
    
    void splay(int x,int&k)
    {
        while(x!=k)
        {
            int y=fa[x],z=fa[y];
            if(y!=k)
            {
                if(c[y][1]==x^c[z][1]==y)rotate(x,k);
                else rotate(y,k);
            }
            rotate(x,k);
        }
    }
    
    int get(int x,double now)
    {
        if(!x)return 0;
        if(L[x]+eps>=now&&now+eps>=R[x]) return x;
        return get(c[x][L[x]+eps>now],now);
    }
    
    void ins(int&now,int last,int k)
    {
        if(!now){now=k;fa[k]=last;return;}
        ins(c[now][x[k]>x[now]+eps],now,k);
    }
    
    int ask_before(int x,int k)
    {
        if(!x)return 0;int q;
        if(getslop(k,x)+eps>=R[x])return (q=ask_before(c[x][0],k))?q:x;
        else return ask_before(c[x][1],k);
    }
    
    int ask_after(int x,int k)
    {
        if(!x)return 0;int q;
        if(getslop(x,k)<=L[x]+eps)return (q=ask_after(c[x][1],k))?q:x;
        else return ask_after(c[x][0],k);
    }
    
    void repair(int k)
    {
        splay(k,rt);
        if(c[k][0])
        {
            int l=ask_before(c[k][0],k);
            if(l)
            {
                splay(l,c[k][0]);c[l][1]=0;
                R[l]=L[k]=getslop(k,l);
            }
            else L[k]=-INF;
        }
        else L[k]=INF;
        if(c[k][1])
        {
            int r=ask_after(c[k][1],k);
            if(r)
            {
                splay(r,c[k][1]);c[r][0]=0;
                L[r]=R[k]=getslop(r,k);
            }
            else R[k]=INF;
        }
        else R[k]=-INF;
        if(L[k]<=R[k]+eps)
        {
            int l=c[k][0],r=c[k][1];rt=l?l:r;
            fa[r]=l;c[l][1]=r;fa[l]=0;
            L[r]=R[l]=getslop(r,l);
        }
    }
    
    int main()
    {
        n=read();scanf("%lf",&f[0]);
        for(int i=1;i<=n;i++)scanf("%lf%lf%lf",&a[i],&b[i],&r[i]);
        for(int i=1;i<=n;i++)
        {
            int j=get(rt,-(a[i]/b[i]));
            f[i]=max(f[i-1],a[i]*x[j]+b[i]*y[j]);
            y[i]=f[i]/(a[i]*r[i]+b[i]);
            x[i]=y[i]*r[i];
            ins(rt,0,i);repair(i);
        }
        printf("%.3lf",f[n]);
        return 0;
    }

     cdq分治 好写多了。

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    #define eps 1e-8
    #define MN 100000
    #define INF 2000000000
    using namespace std;
    inline int read()
    {
        int x = 0 , f = 1; char ch = getchar();
        while(ch < '0' || ch > '9'){ if(ch == '-') f = -1;  ch = getchar();}
        while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
        return x * f;
    }
    
    struct P{
        double a,b,x,y,r,slop;int id;
        bool operator<(const P&b)const{return slop>b.slop;}
    }p[MN+5],t[MN+5];
    double f[MN+5];
    int n,q[MN+5];
    
    double getslop(int x,int y)
    {
        if(!y)return -INF;
        if(fabs(p[x].x-p[y].x)<eps)return INF;
        return (p[y].y-p[x].y)/(p[y].x-p[x].x);
    }
    
    void solve(int l,int r)
    {
        if(l==r)
        {
            f[l]=max(f[l-1],f[l]);
            p[l].y=f[l]/(p[l].a*p[l].r+p[l].b);
            p[l].x=p[l].y*p[l].r;
            return;
        }
        int mid=l+r>>1,cnt1=l,cnt2=mid+1,top=0,j=1;
        for(int i=l;i<=r;i++)
            t[p[i].id<=mid?cnt1++:cnt2++]=p[i];
        for(int i=l;i<=r;i++)p[i]=t[i];
        solve(l,mid);
        for(int i=l;i<=mid;i++)
        {
            while(top>1&&getslop(q[top-1],q[top])<getslop(q[top],i)+eps) --top;
            q[++top]=i;
        }
        for(int i=mid+1;i<=r;i++)
        {
            while(j<top&&getslop(q[j],q[j+1])+eps>p[i].slop) ++j;
            f[p[i].id]=max(f[p[i].id],p[q[j]].x*p[i].a+p[q[j]].y*p[i].b);
        }
        solve(mid+1,r);cnt1=l,cnt2=mid+1;
        for(int i=l;i<=r;i++)
            if(cnt2>r||(cnt1<=mid&&(p[cnt1].x<p[cnt2].x+eps
                     ||(fabs(p[cnt1].x-p[cnt2].x)<eps&&p[cnt1].y<p[cnt2].y+eps))))  t[i]=p[cnt1++];
            else t[i]=p[cnt2++];
        for(int i=l;i<=r;i++)p[i]=t[i];
    }
    
    int main()
    {
        n=read();scanf("%lf",&f[0]);
        for(int i=1;i<=n;i++)
        {
            scanf("%lf%lf%lf",&p[i].a,&p[i].b,&p[i].r);
            p[i].slop=(-p[i].a/p[i].b);p[i].id=i;
        }
        sort(p+1,p+n+1);
        solve(1,n);
        printf("%.3lf
    ",f[n]);
        return 0;
    }
     
     
  • 相关阅读:
    六白话经典算法系列 高速分拣 高速GET
    neu1458 方格取数 dp解法
    自然语言处理---新词发现---微博数据预处理2
    JQuery之初探
    TFS(Team Foundation Server)介绍和入门
    ZooKeeper的学习与应用
    软考之路(六)---数据库---深入浅出 三层模式两级映像
    Open the Lock
    C/C++产生随机数
    RPM安装包-Spec文件參数具体解释与演示样例分析
  • 原文地址:https://www.cnblogs.com/FallDream/p/bzoj1492.html
Copyright © 2011-2022 走看看