[BZOJ3963][WF2011][CDQ分治][斜率优化][DP]MachineWorks

3 篇文章 0 订阅
2 篇文章 0 订阅

被精度卡死……

令d[i]为第i台机器可以购买的日期,f[i]表示第d[i]天的最大收入,
则f[i]=max{f[j]-p[j]+r[j]+(d[i]-d[j]-1)*g[j]}
令A[i]=f[i]-p[i]+r[i]-(d[i]+1)*g[i]
f[i]=A[j]+d[i]g[j],则A[j]=f[i]-d[i]g[j]
那么就用CDQ分治维护一下凸包就行了

#include <cstdio>
#include <iostream>
#include <cmath>
#include <algorithm>
#include <string>
#include <cstring>
#define N 400010
#define eps 1e-12

using namespace std;

typedef long long ll;

ll n,c,d,t,q[N];
ll f[N];
struct stp{
  ll d,p,r,g,w,b;
}A[N],ia[N];
struct Point{
  ll x,y;
  int ct;
  Point(){x=y=ct=0;}
  Point(ll a,ll b):x(a),y(b){}
  friend bool operator <(Point a,Point b){
    return a.x<b.x||(a.x==b.x&&a.y<b.y);
  }
  friend Point operator -(Point a,Point b){
    return Point(a.x-b.x,a.y-b.y);
  }
}p[N],ib[N];

inline double cross(Point a,Point b){
  return 1.0*a.x*b.y-1.0*b.x*a.y;
}

inline int dcmp(double x){
  return fabs(x)<eps?0:(x<0?-1:1);
}

inline bool cmp(stp a,stp b){
  return a.d<b.d;
}

inline ll cal(int x,int y){
  return A[x].d*ib[y].x+ib[y].y;
}

inline double getk(int a,int b){
  return (double)(ib[a].y-ib[b].y)/(double)(ib[a].x-ib[b].x);
}

void solve(int l,int r){
  if(l==r){
    f[l]=max(f[l],f[l-1]);
    return;
  }
  int mid=l+r>>1,t1=0;
  solve(l,mid);
  int head=1,tail=0;
  for(int i=l;i<=mid;i++) if(f[i]>=A[i].p) ib[++t1]=Point(A[i].g,f[i]-A[i].p+A[i].r-1ll*(A[i].d+1)*A[i].g);
  sort(ib+1,ib+t1+1);
  for(int i=1;i<=t1;i++){
    while(head<tail&&dcmp(cross(ib[q[tail]]-ib[q[tail-1]],ib[i]-ib[q[tail-1]]))>=0) tail--;
    q[++tail]=i;
  }
  for(int i=mid+1;i<=r;i++){
    while(head<tail&&cal(i,q[head])<=cal(i,q[head+1])) head++;
    if(head<=tail)f[i]=max(f[i],cal(i,q[head]));
  }
  solve(mid+1,r);
}

int main(){
  while(1){
    memset(f,0,sizeof(f));
    scanf("%lld%lld%lld",&n,&f[0],&d);
    if(!n&&!f[0]&&!d) break;
    for(int i=1;i<=n;i++){
      scanf("%lld%lld%lld%lld",&A[i].d,&A[i].p,&A[i].r,&A[i].g);
    }
    A[++n].b=n;A[n].d=d+1;
    sort(A+1,A+1+n,cmp);
    solve(1,n);
    printf("Case %lld: %lld\n",++t,f[n]);
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值