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

    传送门

    首先有一个显然的贪心,每次操作都要做到底,为了最优不会出现只卖一部分或者只买一部分的操作

    所以设 $f[i]$ 表示前 $i$ 天得到的最大价值,那么对于每一个 $i$,枚举所有 $j<i$,意思就是第 $j$ 天全部买入,第 $i$ 天全部卖出

    显然如果知道 $f[j]$,那么就知道第 $j$  天买入多少

    设 $A$ 买了 $X$, $B$ 买了 $Y$,那么 $f[i]=a[i]*X+b[i]*Y$

    因为 $X,Y$ 只和 $j$ 有关,显然可以斜率优化

    具体就是 $-a[i]*X+f[i]=b[i]*Y$,同除一个 $b[i]$,变成 $-a[i]/b[i]*X+f[i]/b[i]=Y$
    那么 $k=-a[i]/b[i],x=X,b=f[i]/b[i],y=Y$

    自己推一下,容易得出 $X_i=f[i]*r[i]/(a[i]*r[i]+b[i]),Y_i=f[i]/(a[i]*r[i]+b[i])$

    然后因为 $k,x$ 都不单调,所以要用 $CDQ$ 搞

    先按斜率排序,然后 $CQD$ 分治之前按下标拆成两部分,这个具体还是看代码吧...

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<cstring>
    using namespace std;
    typedef long long ll;
    typedef double db;
    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<<1)+(x<<3)+(ch^48); ch=getchar(); }
        return x*f;
    }
    const int N=2e5+7,INF=1e9+7;
    const db eps=1e-9;
    int n,S;
    db f[N];
    struct dat{
        db k,x,y,a,b,r;
        int id;
        inline bool operator < (const dat &tmp) const {
            return k<tmp.k;
        }
    }T[N],tmp[N];
    inline db slope(int i,int j)//求斜率维护凸包
    {
        if(fabs(T[i].x-T[j].x)<=eps) return T[i].y>T[j].y ? INF : -INF;
        return (T[i].y-T[j].y)/(T[i].x-T[j].x);
    }
    inline db Cross(db xa,db ya,db xb,db yb) { return xa*yb-xb*ya; }//用叉积维护凸包会更快
    inline void merge(int l,int r,int mid)//按x归并排序
    {
        int pl=l,pr=mid+1;
        for(int p=l;p<=r;p++)
        {
            if(pl<=mid&& (pr>r||T[pl].x<T[pr].x/*+eps*/) ) tmp[p]=T[pl++];
            else tmp[p]=T[pr++];
        }
        for(int p=l;p<=r;p++) T[p]=tmp[p];
    }
    int Q[N];//栈,维护凸包
    void CDQ(int l,int r)
    {
        if(l==r)//到了最底下,此时f[l]已经被所有f[j](j<l)更新完毕
        {
            f[l]=max(f[l],f[l-1]);//这一天可以不操作
            T[l].y=f[l]/(T[l].a*T[l].r+T[l].b),T[l].x=T[l].y*T[l].r;
            //更新f[l]完才求X[l],Y[l]
            return;
        }
        int mid=l+r>>1,pl=l,pr=mid+1,top=0;
        for(int p=l;p<=r;p++)//按下标分开
        {
            if(T[p].id<=mid) tmp[pl++]=T[p];
            else tmp[pr++]=T[p];
        }
        for(int p=l;p<=r;p++) T[p]=tmp[p];
        CDQ(l,mid); Q[0]=0;//先处理左边
        for(int i=l;i<=mid;i++)//此时左边全部更新完毕,可以维护左边构成的凸包了
        //注意此时左边的T[i].x是有序的,因为每次CDQ结束都会merge
        {
            while( top>1 &&
                Cross(T[i].x-T[Q[top-1]].x,T[i].y-T[Q[top-1]].y,T[Q[top]].x-T[Q[top-1]].x,T[Q[top]].y-T[Q[top-1]].y)<=0 ) top--;
            Q[++top]=i;
        }
        for(int i=mid+1;i<=r;i++)//用左边构成的凸包更新右边,此时右边的斜率是有序的
        {
            while( top>1 && /*T[i].k>=slope(Q[top-1],Q[top])+eps*/ //注释的内容和下一行是等价的,注意eps
                Cross(1,T[i].k,T[Q[top]].x-T[Q[top-1]].x,T[Q[top]].y-T[Q[top-1]].y)<=0 ) top--;
            int j=Q[top]; f[T[i].id]=max(f[T[i].id],T[j].x*T[i].a+T[j].y*T[i].b);//更新
        }
        CDQ(mid+1,r); merge(l,r,mid);//处理右边后按x排序
    }
    int main()
    {
        n=read(); f[0]=read();
        for(int i=1;i<=n;i++)
        {
            scanf("%lf%lf%lf",&T[i].a,&T[i].b,&T[i].r);
            T[i].k=-T[i].a/T[i].b; T[i].id=i;//初始化
        }
        sort(T+1,T+n+1); CDQ(1,n);//先按k排序再CDQ
        printf("%.3lf",f[n]);
        return 0;
    }
  • 相关阅读:
    中文短文本分类
    词袋和词向量模型
    【NLP-2017-SA】翻译-Recurrent Attention Network on Memory for Aspect Sentiment Analysis(2017emnlp)
    过拟合和欠拟合问题总结
    【Docker-6】-Docker删除运行过的镜像
    【Docker-5】-Docker运行自己的镜像
    【Docker-3】-Docker入门命令解读
    【Docker-4】-Docker启动nginx
    【Docker-2】windows下安装docker
    【Docker-1】docker简介
  • 原文地址:https://www.cnblogs.com/LLTYYC/p/10740705.html
Copyright © 2011-2022 走看看