bzoj3963 [WF2011]MachineWorks(CDQ+dp)

14 篇文章 0 订阅

Description

你是任意性复杂机器公司(Arbitrarily Complex Machines, ACM)的经理,公司使用更加先进的机械设备生产先进的机器。原来的那一台生产机器已经坏了,所以你要去为公司买一台新的生产机器。你的任务是在转型期内尽可能得到更大的收益。在这段时间内,你要买卖机器,并且当机器被ACM公司拥有的时候,操控这些机器以获取利润。因为空间的限制,ACM公司在任何时候都只能最多拥有一台机器。
在转型期内,有若干台可能卖出的机器。作为先进机器的专家,对于每台机器Mi,你已经知道了其价格Pi和可以买入的日期Di。注意,如果不在第Di天买入机器Mi,那么别的人也会买走这一台机器,也就是说,以后你将没有机会购买这台机器了。如果ACM的钱低于一台机器的价格,那么你显然不可能买到这一台机器。
如果你在第Di天买入了机器Mi,那么ACM公司可以从第(Di)+1天开始使用这一台机器。每使用这台机器一天,就可以为公司创造出Gi美元的收益。
你可以决定要在买入之后的某一天,以一定的折扣价卖出这一台机器。收购市场对于每一台机器,都有一个折扣价Ri。你不能在卖出的那一天使用机器,但是你可以在卖出的那一天再买入一台新的。
在转型期结束后,ACM公司会卖掉当前所拥有的机器。你的任务就是最大化转型期间ACM公司可以得到的收入。

Input

输入包含若干组测试用例。每一组测试用例的第一行有3个正整数N,C和D。N是将会卖出的机器的台数(N<=10^5),C是在转型期开始时公司拥有的美元数量(C<=10^9),D是转型期持续的天数(D<=10^9)。
之后的N行每一行描述了一台机器的情况。每一行有4个正整数Di,Pi,Ri和Gi,分别表示这台机器卖出的时间,购买这台机器需要的美元数量,卖出这台机器的折扣价和使用这台机器可以得到的利润。这些数字满足1<=Di<=D,1<=Ri<Pi<=10^9且1<=Gi<=10^9.
最后一组测试用例后面的一行由3个0组成,表示输入数据。

Output

对于每一组测试用例,输出测试用例的编号,之后给出ACM公司在第D+1天结束后可以得到的最大数量的美元。
请依照下面给出的样例输出。

Sample Input

6 10 20

6 12 1 3

1 9 1 2

3 2 1 2

8 20 5 4

4 11 7 4

2 10 9 1

0 0 0

Sample Output

Case 1: 44

[ Submit][ Status][ Discuss]



分析:
将所有的机器按照从小到大排序

假设你买到了一台好的机器,在下一个机器进来之前,你肯定是一直运转下去的

f[i] f [ i ] 表示在 d[i] d [ i ] 卖掉手里的机器后的最大收益,枚举买入新机器的时间 j j

f[i]=max(f[i1],f[j]+(d[i]d[j]1)g[j]p[j]+r[j])

其中 f[j]>=p[i] f [ j ] >= p [ i ]
复杂度 O(n2) O ( n 2 ) ,,想办法优化

仔细观察一下状态注意方程,实际上是这样的形式: f[i]=max(f[i1],f[j]+w(i,j)) f [ i ] = m a x ( f [ i − 1 ] , f [ j ] + w ( i , j ) )
可以考虑用斜率优化:

f[k]+(d[i]d[k]1)g[k]p[k]+r[k]>=f[j]+(d[i]d[j]1)g[j]p[j]+r[j] f [ k ] + ( d [ i ] − d [ k ] − 1 ) ∗ g [ k ] − p [ k ] + r [ k ] >= f [ j ] + ( d [ i ] − d [ j ] − 1 ) ∗ g [ j ] − p [ j ] + r [ j ]

f[k](d[k]+1)g[k]p[k]+r[k](f[j](d[j]+1)g[j]p[j]+r[j])>=d[i](g[j]g[k]) f [ k ] − ( d [ k ] + 1 ) ∗ g [ k ] − p [ k ] + r [ k ] − ( f [ j ] − ( d [ j ] + 1 ) ∗ g [ j ] − p [ j ] + r [ j ] ) >= d [ i ] ∗ ( g [ j ] − g [ k ] )

f[k](d[k]+1)g[k]p[k]+r[k](f[j](d[j]+1)g[j]p[j]+r[j])g[j]g[k]>=d[i] f [ k ] − ( d [ k ] + 1 ) ∗ g [ k ] − p [ k ] + r [ k ] − ( f [ j ] − ( d [ j ] + 1 ) ∗ g [ j ] − p [ j ] + r [ j ] ) g [ j ] − g [ k ] >= d [ i ]

但是 g g 不单调,不能直接用单调队列,不过容易知道这个点一定在一个上凸壳上,考虑CDQ分治
对于一个区间[l,r],先更新 [l,mid] [ l , m i d ]
然后对左边这部分的区间的点集, 进行排序,形成上凸壳
在更新右边区间的时候,由于斜率是递减的,所以我们只需要扫一遍这个图形即可完成所有右边区间值的更新
然后就这样递归着去更新,完成CDQ分治

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 100033
#define eps 1e-9
#define ll long long
using namespace std;
const double inf=1e19;
int n,C,D,top,s[N];
ll f[N];
struct data{
    ll d,r,p,g,id;
}a[N],po[N];
struct point{
    ll x,y; bool pd;
    bool operator <(const point &b) const {
      return x<b.x||(x==b.x&&y>b.y);
    } 
}p[N],q[N];

int cmp(data a,data b)
{
    return a.d<b.d;
}

double get(int i,int j) {
    if (p[i].x-p[j].x==0) return -inf;
    return (double)(p[i].y-p[j].y)/(double)(p[i].x-p[j].x);
}

void CDQ(int l,int r)
{
    if (l==r) {
        f[l]=max(C-a[l].p,f[l]); p[l].pd=0;
        if (f[l]<0) f[l]=-1,p[l].pd=1;
        p[l].x=a[l].g;
        p[l].y=f[l]-a[l].d*a[l].g-a[l].g+a[l].r;
        return;
    }
    int mid=(l+r)/2;
    int t1=l; int t2=mid+1;
    for (int i=l;i<=r;i++)
     if (a[i].id<=mid) po[t1++]=a[i];
     else po[t2++]=a[i];
    for (int i=l;i<=r;i++) a[i]=po[i];
    CDQ(l,mid);
    int top=0;
    for (int i=l;i<=mid;i++) {
        if (p[i].pd) continue;
        while (top>1&&get(i,s[top])+eps>get(s[top],s[top-1])) top--;
        s[++top]=i;
    }
    if (top) {
        int j=1;
        for (int i=mid+1;i<=r;i++) {
            while (j<top&&get(s[j],s[j+1])>-a[i].d) 
             j++;
            int k=s[j];
            f[a[i].id]=max(f[a[i].id],p[k].y+p[k].x*a[i].d-a[i].p);
        }
    }
    CDQ(mid+1,r);
    t1=l,t2=mid+1;
    for (int i=l;i<=r;i++)
        if ((p[t1]<p[t2]||t2>r)&&t1<=mid) q[i]=p[t1++];
        else q[i]=p[t2++];
    for (int i=l;i<=r;i++)
        p[i]=q[i];
}
int main()
{
    int T=0;
    while (scanf("%d%d%d",&n,&C,&D)!=EOF&&n+C+D) {
        T++;
        for (int i=1;i<=n;i++) scanf("%lld%lld%lld%lld",&a[i].d,&a[i].p,&a[i].r,&a[i].g);
        n++; a[n].d=D+1; a[n].p=0; a[n].r=0; a[n].g=0;
        for (int i=1;i<=n;i++) f[i]=-1;
        sort(a+1,a+1+n,cmp);
        for (int i=1;i<=n;i++) a[i].id=i;
        CDQ(1,n);
        printf("Case %d: %lld\n",T,f[n]);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值