[BZOJ3963][WF2011]MachineWorks(斜率优化dp+cdq分治)

题目:

我是超链接

题解:

这题目有点眼熟啊。。。莫不是货币兑换?
那根据套路来一波dp,f[i]表示到第D[i]天卖掉所有机器得到的最多金钱,显然f[0]=c
f[i]=max(f[i1],f[j]P[j]+(D[i]D[j]1)G[j]+R[j])
列完方程我们可以知道,之所以这样设计是因为读入的D[i]影响的是前一群机器而不是这一个(因为还没开始用。)
f[i]=f[j]P[j]+(D[i]D[j]1)G[j]+R[j]
f[j]P[j](D[j]+1)G[j]+R[j]=D[i]G[j]+f[i]
那就是斜率固定-D[i],使截距f[i]最大,把之前的看做一个点(G[j],f[j]-P[j]-(D[j]+1)*G[j]+R[j]),那就维护一个上凸壳用cdq分治求解

注意比较的时候不能光比较x,还要把y比上
INF要设的大一点= =

代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
using namespace std;
const double INF=1e18;   ////
const int N=100005;
struct hh{int id;LL x,y,d,p,r,g,k;}Q[N],jl[N];
int stack[N],n;
LL f[N],d,c;
double getk(hh a,hh b)
{
    if (a.x==b.x) return -INF;
    return (double)(a.y-b.y)/(double)(a.x-b.x);
}
int cmpx(hh a,hh b){return a.x<b.x||(a.x==b.x&&a.y>b.y);} ////
void merge(int l,int r)
{
    int mid=(l+r)>>1,t1=l,t2=mid+1;
    for (int i=l;i<=r;i++)
      if ((t1<=mid && cmpx(Q[t1],Q[t2])) || t2>r) jl[i]=Q[t1++];
      else jl[i]=Q[t2++];
    for (int i=l;i<=r;i++) Q[i]=jl[i];
}
void cdq(int l,int r)
{
    if (l==r)
    {
        f[l]=max(f[l],f[l-1]);
        Q[l].x=Q[l].g; 
        if (f[l]>=Q[l].p) Q[l].y=f[l]-Q[l].p-(LL)(Q[l].d+1)*Q[l].g+Q[l].r;
        else Q[l].y=-INF;
        return; 
    }
    int mid=(l+r)>>1,t1=l,t2=mid+1;
    for (int i=l;i<=r;i++)
      if (Q[i].id<=mid) jl[t1++]=Q[i];
      else jl[t2++]=Q[i];
    for (int i=l;i<=r;i++) Q[i]=jl[i];
    cdq(l,mid);
    int top=0;
    for (int i=l;i<=mid;i++)   
      if (Q[i].y!=-INF)
      {
        while (top>1 && getk(Q[i],Q[stack[top]])>=getk(Q[stack[top-1]],Q[stack[top]])) top--;
        stack[++top]=i;
      }
    for (int i=mid+1;i<=r;i++)
    {
        while (top>1 && getk(Q[stack[top]],Q[stack[top-1]])<jl[i].k) top--;
        int j=stack[top];
        f[jl[i].id]=max(f[jl[i].id],Q[j].y-(LL)jl[i].k*Q[j].x);
    }
    cdq(mid+1,r); merge(l,r);
}
int cmpk(hh a,hh b){return a.k<b.k;}
int cmpd(hh a,hh b){return a.d<b.d;}
int main()
{
    int id=0;scanf("%d%lld%lld",&n,&c,&d);
    while (n&&c&&d)
    {
        memset(f,0,sizeof(f));f[1]=c;
        for (int i=1;i<=n;i++) scanf("%lld%lld%lld%lld",&Q[i].d,&Q[i].p,&Q[i].r,&Q[i].g);
        sort(Q+1,Q+n+1,cmpd);
        for (int i=1;i<=n;i++) Q[i].id=i,Q[i].k=-Q[i].d;
        Q[++n].d=d+1; Q[n].k=-Q[n].d; Q[n].id=n;
        sort(Q+1,Q+n+1,cmpk);
        cdq(1,n);
        printf("Case %d: %lld\n",++id,f[n]);
        scanf("%d%lld%lld",&n,&c,&d);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值