• 【BZOJ】3963: [WF2011]MachineWorks


    【题意】给定n台在时间di可以买入的机器,pi买入,可在任意时间ri卖出,买入和卖出之间的持有时间每天产生gi金钱,任意时间至多持有一台机器。给定初始钱数c和总天数T,求最大收益。n<=10^5。

    【算法】动态规划+斜率优化(CDQ分治)

    【题解】机器按di排序,添加一台时间为T+1的机器,令f[i]表示di时不持有机器(最后一台机器在di之前卖出)的最大收益。

    f[i]=max{ f[i-1] , f[j]-p[j]+r[j]+g[j]*(d[i]-d[j]-1) } , j<i

    为了简化方程,令A[i]=f[j]-p[j]+r[j]-g[j]*(d[j]+1),则

    f[i]=max{ f[i-1] , A[j]+g[j]*d[i] } , j<i

    对于g[j]<g[k]的两个决策j和k,当k优于j时满足:

    A[j]+g[j]*d[i]<A[k]+g[k]*d[i] 即 (A[j]-A[k])/(g[j]-g[k])>-d[i]

    用CDQ分治维护上凸包,先按-d[i]排序,然后左子区间构造凸包(按g[]排序),右子区间顺序决策(按-d[i]排序),最后按x[]归并排序。

    具体过程见CDQ分治维护斜率优化

    复杂度O(n log n)。

    #include<cstdio>
    #include<cctype>
    #include<algorithm>
    #define ll long long
    using namespace std;
    int read(){
        char c;int s=0,t=1;
        while(!isdigit(c=getchar()))if(c=='-')t=-1;
        do{s=s*10+c-'0';}while(isdigit(c=getchar()));
        return s*t;
    }
    const int maxn=100010;
    const double eps=1e-10,inf=1000000000000;
    int s[maxn],n,m;
    ll f[maxn];
    struct cyc{int d,p,r,g,id,x;ll y;}a[maxn],b[maxn];
    bool cmp(cyc a,cyc b){return -a.d<-b.d;}
    double slope(int A,int B){
        if(a[A].x==a[B].x){if(a[B].y<a[A].y)return inf;else return -inf;}
        return 1.0*(a[A].y-a[B].y)/(a[A].x-a[B].x);
    }
    void CDQ(int l,int r){
        if(l==r){
            f[l]=max(f[l-1],f[l]);
            a[l].x=a[l].g;
            a[l].y=f[l]-a[l].p+a[l].r-1ll*a[l].g*(a[l].d+1);
            if(f[l]<a[l].p)a[l].g=-1;
            return;
        }
        int mid=(l+r)>>1;
        int x1=l-1,x2=mid;
        for(int i=l;i<=r;i++)if(a[i].id<=mid)b[++x1]=a[i];else b[++x2]=a[i];
        for(int i=l;i<=r;i++)a[i]=b[i];
        CDQ(l,mid);
        int top=0;
        for(int i=l;i<=mid;i++)if(~a[i].g){
            while(top>1&&slope(s[top],i)<slope(s[top-1],s[top]))top--;
            s[++top]=i;
        }
        int x=1;
        for(int i=mid+1;i<=r;i++){
            while(x<top&&slope(s[x],s[x+1])<-a[i].d)x++;
            if(x<=top)f[a[i].id]=max(f[a[i].id],a[s[x]].y+1ll*a[s[x]].g*a[i].d);
        }
        CDQ(mid+1,r);
        x1=l,x2=mid+1;
        for(int i=l;i<=r;i++){
            if(x1==mid+1)b[i]=a[x2++];else
            if(x2==r+1)b[i]=a[x1++];else
            if(a[x1].g>a[x2].g)b[i]=a[x1++];else b[i]=a[x2++];
        }
        for(int i=l;i<=r;i++)a[i]=b[i];
    }
    int main(){
        n=read();f[0]=read();m=read();
        int T=0;
        while(n||f[0]||m){
            T++;
            for(int i=1;i<=n;i++)a[i].d=read(),a[i].p=read(),a[i].r=read(),a[i].g=read();
            sort(a+1,a+n+1,cmp);
            for(int i=1;i<=n;i++)a[i].id=n-i+1;
            a[++n]=(cyc){m+1,0,0,0,n,0,0};
            sort(a+1,a+n+1,cmp);
            for(int i=1;i<=n;i++)f[i]=0;
            CDQ(1,n);
            printf("Case %d: %lld
    ",T,f[n]);
            n=read();f[0]=read();m=read();
        }
        return 0;
    }
    View Code

    代码中使用的方程时(A[j]-A[k])/(g[j]-g[k])<-d[i] ,g[j]>g[k]。

  • 相关阅读:
    IP通信基础学习第八周
    IP通信基础学习第七周(下)
    IP通信基础学习第七周(上)
    IP通信基础学习第六周(下)
    mysql 笔记
    一个不错的MYSQL数据库备份类,PHP版,一个文件,精简版
    Linux下apache日志(按日期存放)分析与状态查看方法
    呵呵
    docker-compose常用命令
    mysql 数据备份
  • 原文地址:https://www.cnblogs.com/onioncyc/p/8253066.html
Copyright © 2020-2023  润新知