hdu 3842 Machine Works

来源:互联网 发布:java导出文件到桌面 编辑:程序博客网 时间:2024/06/05 15:27

题目链接

题目大意

有一些机器,可以在di时将它买入,价格为pi,在一段时间后可以以ri的价格卖出,机器运作时每天收入为gi
机器在买入和卖出当天不可以运作,但是可以在同一天买入一台机器并卖出另一台机器。
厂房里只能有一台机器。
在n+1天时,你必须卖掉手头的机器。
。。。
还有一堆条件,详见题目。

题解

这是一道cdq分治优化dp的题目,和cash那题差不多。
首先推出dp方程:
定义dp[i]表示在第di天时卖掉手头的机器后的钱数。
那么可以知道
dp[i]=Max{dp[i1],dp[j]p[j]+r[j]+(d[i]d[j])g[j]}
条件是dp[j]>p[j]
可以先假定A[i]=dp[i]p[i]+r[i]
那么,对于两个数j,k如果j比k优,就有
A[j]+(d[i]d[j])g[j]>A[k]+(d[i]d[k])g[k]
通过移项,可以将关于i的限制条件换到一边,于是得到:
B[i]=A[i](D[i]+1)G[i]
Di<BkBjGjGk
同时可推出新的dp方程B[j]=d[i]g[j]+dp[i]
可以将(g[j],B[j])看做平面上的点,答案就是求过一些点,且斜率为-d[i]的直线中,截距的最大值。
于是可以维护一个凸包,然后更新答案。
维护这个凸包要先按g排序,然后在维护,但是dp过程中不可能每次都sort一遍,所以这时候就需要cdq分治。
cdq分治,简而言之,就是首先递归计算左边一半的值,然后利用左边一半的值去更新右边一半。
于是问题就变得明朗了,每次先把左边一半递归计算,然后把左边一半维护成一个凸包,然后更新右边一半的dp值。
由于题目要求最后一天必须卖掉手头的机器,可以虚设一个n+1点,答案就是dp[n+1]。
同时可以虚设一个0结点作为开始。

const int M=100005;#define fi first#define se secondtypedef long long ll;typedef pair<int,ll> pii;inline void rd(int&res){    res=0;char c;    while(c=getchar(),c<48);    do res=res*10+(c^48);    while(c=getchar(),c>47);}struct node{    int d,p,r,g;    //卖出时间,价格,折扣价.利润    void input(){        rd(d);rd(p);rd(r);rd(g);    }}mac[M];ll dp[M];bool cmp(node a,node b){    return a.d<b.d;}inline ll calc(int x){    return dp[x]+mac[x].r-(ll)(mac[x].d+1)*mac[x].g-mac[x].p;}pii q[M],stk[M];inline bool slope(pii a,pii b,pii c){    return (double)(a.se-b.se)*(c.fi-a.fi)<(double)(b.fi-a.fi)*(a.se-c.se);}void solve(int L,int R){    if(L==R) return;    int mid=(L+R)>>1;    solve(L,mid);    int tot=0,top=0,j=0;    for(int i=L;i<=mid;i++)        if(dp[i]>=mac[i].p) q[tot++]=pii(mac[i].g,calc(i));    sort(q,q+tot);    for(int i=0;i<tot;i++){        while(top>1&&!slope(stk[top-1],stk[top],q[i])) top--;        stk[++top]=q[i];    }    for(int i=mid+1;i<=R;i++){        ll b1,b2,a1,a2,x;        x=mac[i].d;        while(j<top){            a1=stk[j].fi;b1=stk[j].se;            a2=stk[j+1].fi;b2=stk[j+1].se;            if(b2+(ll)a2*x<=b1+(ll)a1*x) break;            j++;        }        dp[i]=max(dp[i],stk[j].fi*x+stk[j].se);    }    solve(mid+1,R);}int main(){    int n,D,cas=0;    while(scanf("%d%lld%d",&n,&dp[0],&D)!=EOF,n||dp[0]||D){        for(int i=1;i<=n;i++)            mac[i].input();        sort(mac+1,mac+n+1,cmp);        n++;        mac[n].d=D+1;        mac[n].g=mac[n].p=0;        for(int i=1;i<=n;i++)            dp[i]=dp[0];        solve(0,n);        printf("Case %d: %lld\n",++cas,dp[n]);    }    return 0;}
0 0