【BZOJ3963】【ACM-WF2011】MachineWorks(CDQ分治+斜率优化)

1 篇文章 0 订阅
1 篇文章 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

题解:
戳这里
我觉得这是讲的非常好的一篇,非常清晰,在这里推荐一下。
感觉证明单调性还是太弱了,不会证,只会猜= =b。
代码如下:

#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<math.h>
#define ll long long
#define inf 0x7f7f7f7f
#define N 100005
#define pii pair<int,ll> 
#define X first
#define Y second
using namespace std;
ll read()
{
    ll x=0,f=1;
    char c=getchar();
    while(c<'0' || c>'9') {if(c=='-') f=-1;c=getchar();}
    while(c<='9' && c>='0') {x=x*10+c-'0';c=getchar();}
    return x*f;
}
struct nod
{
    int d,p,r,g;
    bool operator < (const nod& b) const {return d<b.d;}
}a[N];
int cas,n,d;
ll f[N];  
pii q[N],b[N];  
ll h(int j) 
{  
    return f[j]+(ll)a[j].r-(ll)a[j].p-(ll)a[j].g*(ll)(a[j].d + 1);  
}  
int get_slope(pii a, pii b, pii c) 
{  
    ll xa=b.X-a.X,xb=c.X-a.X,ya=b.Y-a.Y,yb=c.Y-a.Y;  
    double tmp=(double)xa*yb-(double)xb*ya;  
    return tmp<0;  
}  
void cdq(int l,int r)
{
    if(l>=r) return;
    int mid=(l+r)>>1;
    cdq(l,mid);
    int cnta=0,cntb=0;
    for(int i=l;i<=mid;i++)
    if(f[i]>=a[i].p) q[cnta++]=pii(a[i].g,h(i));
    sort(q,q+cnta);
    for(int i=0;i<cnta;i++)
    {
        while(cntb>1 && !get_slope(b[cntb-1],b[cntb],q[i])) 
        cntb--;
        b[++cntb]=q[i];
    }
    int j=0;
    for(int i=mid+1;i<=r;i++)
    {
        ll x=a[i].d;
        ll a1,a2,b1,b2;
        while(j<cntb)
        {
            a1=b[j].X,a2=b[j+1].X;
            b1=b[j].Y,b2=b[j+1].Y;
            if(a1*x+b1>=a2*x+b2) break;
            j++;
        }
        f[i]=max(f[i],(ll)b[j].X*x+(ll)b[j].Y);
    }
    cdq(mid+1,r);
}
int main()
{
    while(1)
    {
        n=read(),f[0]=read(),d=read();
        if(!n) break;
        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+1+n);
        a[++n].d=d+1,a[n].p=a[n].g=0;
        for(int i=1;i<=n;i++) f[i]=f[0];
        cdq(0,n);
        printf("Case %d: %lld\n",++cas,f[n]);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值