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

题目描述

传送门

题解

首先考虑如何暴力dp
肯定是先按照D(i)排序对吧
令f(i)表示在第D(i)天卖掉手里有的机器所能得到的最大收入
那么有一个很显然的dp方程:f(i)=max{f(i-1),max{f(j)-P(j)+G(j)*(D(i)-D(j)-1)+R(j)}}(1<=j<=i-1)
只考虑后半部分,实际上就变成了f(i)=max{f(j)-P(j)+G(j)*(D(i)-D(j)-1)+R(j)}(1<=j<=i-1)
将这个式子变一下形式:f(i)=f(j)-P(j)+G(j)*D(i)-G(j)*D(j)-G(j)+R(j)->令y(j)=f(j)-P(j)-G(j)*D(j)-G(j)+R(j),x(j)=G(j)->f(i)=D(i)*x(j)+y(j)
我们发现这个式子是可以斜率优化的,也就是y(j)=-D(i)*x(j)+f(i);这样就可以看成是一条直线,斜率为-D(i),截距为f(i),现在要选出一个点使截距最大
斜率不满足单调性T_T;不过容易知道这个点一定在一个上凸壳上,那么用splay维护一个上凸壳就行了
用cdq分治的写法更加方便

代码

#include
#include
#include
#include
#include
using namespace std;
#define N 100005
#define LL long long

const double inf=1e18;
int Case,n;long double C,D;
struct data
{
    long double d,p,r,g,k,x,y;
    int id;
    bool flag;
}q[N],a[N],b[N];
int stack[N];
long double f[N];

long double read()
{
    int x=0;char ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return (long double)x;
}
void clear()
{
    memset(f,0,sizeof(f));
    memset(stack,0,sizeof(stack));
    for (int i=1;i<=n+1;++i)
    {
        q[i].d=q[i].p=q[i].r=q[i].g=0,q[i].id=0,q[i].flag=0,q[i].k=q[i].x=q[i].y=0;
        a[i].d=a[i].p=a[i].r=a[i].g=0,a[i].id=0,a[i].flag=0,a[i].k=a[i].x=a[i].y=0;
        b[i].d=b[i].p=b[i].r=b[i].g=0,b[i].id=0,b[i].flag=0,b[i].k=b[i].x=b[i].y=0;
    }
}
int cmpd(data a,data b)
{
    return a.dint cmpx(data a,data b)
{
    return a.xx||(a.x==b.x&&a.y>b.y);
}
int cmpk(data a,data b)
{
    return a.k>b.k;
}
double getk(data a,data b)
{
    double ax=a.x,ay=a.y,bx=b.x,by=b.y;
    if (ax==bx) return -inf;
    return (ay-by)/(ax-bx);
}
void cdq(int l,int r)
{
    int mid=(l+r)>>1;
    if (l==r)
    {
        f[l]=max(f[l],f[l-1]);
        if (f[l]>=q[l].p)
        {
            q[l].flag=1;
            q[l].x=q[l].g;
            q[l].y=f[l]-q[l].p-q[l].g*q[l].d-q[l].g+q[l].r;
        }   
        return;
    }
    cdq(l,mid);
    int acnt=0,bcnt=0;
    for (int i=l;i<=mid;++i) a[++acnt]=q[i];
    for (int i=mid+1;i<=r;++i) b[++bcnt]=q[i];
    sort(a+1,a+acnt+1,cmpx);sort(b+1,b+bcnt+1,cmpk);
    int top=0;
    for (int i=1;i<=acnt;++i)
        if (a[i].flag)
        {
            while (top>1&&getk(a[i],a[stack[top]])>=getk(a[stack[top]],a[stack[top-1]]))
                --top;
            stack[++top]=i;
        }
    int now=1;
    for (int i=1;i<=bcnt;++i)
    {
        while (now1]])>=b[i].k)
            ++now;
        int t=stack[now];
        f[b[i].id]=max(f[b[i].id],(LL)a[t].x*b[i].d+(LL)a[t].y);
    }
    cdq(mid+1,r);
}
int main()
{
    while (~scanf("%d",&n))
    {
        if (!n) break;
        C=read();D=read();
        clear();
        for (int i=1;i<=n;++i)
        {
            q[i].d=read();q[i].p=read();q[i].r=read();q[i].g=read();
            q[i].k=-q[i].d;
        }
        sort(q+1,q+n+1,cmpd);
        q[++n].d=D+1,q[n].p=0,q[n].r=0,q[n].g=0,q[n].k=-q[n].d;
        for (int i=1;i<=n;++i) q[i].id=i;
        f[1]=C;
        cdq(1,n);
        printf("Case %d: %lld\n",++Case,(long long)f[n]);
    }
}

你可能感兴趣的:(题解,dp,cdq分治/整体二分)