[BZOJ2726][SDOI2012]任务安排(斜率优化dp+cdq分治)

来源:互联网 发布:淘宝的全球购在哪 编辑:程序博客网 时间:2024/05/17 02:33

题目描述

传送门

题解

哈哈哈速度实力倒数第一
也不知为什么写的奇慢无比卡常数卡到丧心病狂才勉强过了
我分明是把sort都搞成归并了呀…

这题是dp+cdq分治好题啊

首先考虑最裸的dp怎么搞
f(i)表示前i个搞完的最小花费…
等等,时间有后效性…记录一下时间?
瞬间爆炸
实际上我们可以发现,只要知道一共划分成了几部分,就能通过T的前缀和以及s计算出时间,所以令f(i,j)表示划分成i段,划分到第j个,最小花费
f(i,j)=min{f(i-1,k)+(sT(j)+s*i)*(sF(j)-sF(k))}(0<=k<=j-1)其中sF和sT分别表示F和T的前缀和
但是这样复杂度是O(n3)的,并且是二维dp,根本无法怎么优化

然后再考虑稍微不裸一点的dp怎么搞
f(i)表示前i个搞完的最小花费…
不是说了时间有后效性嘛…可是必须得是一维dp不能是二维的啊…所以这里就需要机智一下了
假设我们将整个序列划分成了m段,每一段的F的和依次是p1,p2..pm,每一段结束的时间(不算s)分别是t1,t2…tm,那么实际上这种划分方式的花费就是(s+t1)*p1+(2*s+t2)*p2+…+(m*s+tm)*pm
将这个式子拆开s*p1+2*s*p2+…+m*s*pm+t1*p1+t2*p2+…+tm*pm=s*(p1+2*p2+3*p3+…+m*pm)+t1*p1+t2*p2+…+tm*pm
观察前面的那个式子,系数是等差的,其实稍微转化一下就是另一种形式:(p1+p2+p3+…+pm)+(p2+p3+…+pm)+(p3+…+pm)+…+(pm)
其实就是F的后缀和!
这样的话,我们就非常完美地将s所带来的影响消除了,状态转移方程就可以写为:f(i)=max{f(j)+sT(i)*sF(j+1)+s*sF(j+1)}(0<=j<=i-1),其中sT(i)表示T的前缀和,sF(i)表示F的后缀和
时间复杂度O(n2)

md如果这题时间没有负数不就是一个裸斜率优化嘛…
但是出题人说:“对时间的理解不要那么狭隘嘛”
于是一掌把我打回cdq分治
下面就是斜率优化+cdq分治的常规套路了
f(i)=f(j)+sT(i)*sF(j+1)+s*sF(j+1),令y(j)=f(j)+s*sF(j+1),x(j)=sF(j+1)
那么f(i)=sT(i)*x(j)+y(j),y(j)=-sT(i)*x(j)+f(i)
这样就可以看成是一条斜率为-sT(i),截距为f(i)的直线,要使f(i)最小,实际上就是要选择一个点使直线的纵截距最小
显然可能成为答案的点都在一个下凸壳上,我们可以选择用splay维护凸包然后每一次二分,或者写(bu)好写(bu)好调(tle)的cdq分治

代码

O(n3)

#include<algorithm>#include<iostream>#include<cstring>#include<cstdio>#include<cmath>using namespace std;#define LL long long#define N 1005int n;LL s,ans,T[N],F[N],sT[N],sF[N],f[N][N],g[N];int main(){    scanf("%d%d",&n,&s);    for (int i=1;i<=n;++i) scanf("%I64d%I64d",&T[i],&F[i]);    for (int i=1;i<=n;++i) sT[i]=sT[i-1]+T[i],sF[i]=sF[i-1]+F[i];    memset(f,127,sizeof(f));ans=f[0][0];f[0][0]=0;    for (int i=1;i<=n;++i)        for (int j=i;j<=n;++j)        {            for (int k=0;k<j;++k)                f[i][j]=min(f[i][j],f[i-1][k]+(sT[j]+s*(LL)i)*(sF[j]-sF[k]));        }    for (int i=1;i<=n;++i)        for (int j=1;j<=i;++j) g[i]=min(g[i],f[j][i]);    for (int i=1;i<=n;++i) printf("%d : %I64d\n",i,g[i]);    printf("%I64d\n",g[n]);}

O(n2)

#include<algorithm>#include<iostream>#include<cstring>#include<cstdio>#include<cmath>using namespace std;#define LL long long#define N 300005int n;LL s,T[N],F[N],sT[N],sF[N],f[N];int d[N];int main(){    scanf("%d%d",&n,&s);    for (int i=1;i<=n;++i) scanf("%I64d%I64d",&T[i],&F[i]);    for (int i=1;i<=n;++i) sT[i]=sT[i-1]+T[i];    for (int i=n;i>=0;--i) sF[i]=sF[i+1]+F[i];    memset(f,127,sizeof(f));f[0]=0;    for (int i=1;i<=n;++i)    {        for (int j=0;j<i;++j)            if (f[j]+sT[i]*sF[j+1]+s*sF[j+1]<f[i])                f[i]=f[j]+sT[i]*sF[j+1]+s*sF[j+1];        f[i]-=sT[i]*sF[i+1];    }    printf("%I64d\n",f[n]);}

O(nlogn)

#include<algorithm>#include<iostream>#include<cstring>#include<cstdio>#include<cmath>using namespace std;#define N 300005#define LL long longconst int inf=1e9;int n,s,T[N],F[N],sT[N],sF[N];LL f[N];struct data{int k,x;LL y;int id;}q[N],p[N],nq[N],np[N];int stack[N];int read(){    int x=0,f=1;char ch=getchar();    while (ch<'0'||ch>'9')    {        if (ch=='-') f=-1;        ch=getchar();    }    while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();    return x*f;}int cmpx(data a,data b){    return a.x<b.x||(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){    if (a.x==b.x) return -inf;    double ax=(double)a.x,ay=(double)a.y,bx=(double)b.x,by=(double)b.y;    return (ay-by)/(ax-bx);}inline void cdq(int l,int r){    if (l==r)    {        f[l]-=(LL)sT[l]*sF[l+1];        q[l].y=f[l]+(LL)s*sF[l+1];        p[l].y=f[l]+(LL)s*sF[l+1];        return;    }    int mid=(l+r)>>1;    int ql=l,qr=mid+1,pl=l,pr=mid+1;    for (int i=l;i<=r;++i)    {        if (q[i].id<=mid) nq[ql++]=q[i];        else nq[qr++]=q[i];        if (p[i].id<=mid) np[pl++]=p[i];        else np[pr++]=p[i];    }    for (int i=l;i<=r;++i) q[i]=nq[i],p[i]=np[i];    cdq(l,mid);    int top=0;    for (int i=l;i<=mid;++i)    {        while (top>1&&getk(q[i],q[stack[top]])<=getk(q[stack[top-1]],q[stack[top]]))            --top;        stack[++top]=i;    }    int now=1;    for (int i=mid+1;i<=r;++i)    {        while (now<top&&getk(q[stack[now]],q[stack[now+1]])<=p[i].k)            ++now;        int r=stack[now],t=p[i].id;        LL Min=(LL)q[r].x*sT[t]+q[r].y;        if (Min<f[t]) f[t]=Min;    }    cdq(mid+1,r);    pl=l,pr=mid+1,now=l;    while (pl<=mid&&pr<=r)    {        if (q[pl].x<q[pr].x) nq[now++]=q[pl++];        else nq[now++]=q[pr++];    }    while (pl<=mid) nq[now++]=q[pl++];    while (pr<=r) nq[now++]=q[pr++];    pl=l,pr=mid+1,now=l;    while (pl<=mid&&pr<=r)    {        if (p[pl].k<p[pr].k) np[now++]=p[pl++];        else np[now++]=p[pr++];    }    while (pl<=mid) np[now++]=p[pl++];    while (pr<=r) np[now++]=p[pr++];    for (int i=l;i<=r;++i) q[i]=nq[i],p[i]=np[i];}int main(){    n=read();s=read();    for (int i=1;i<=n;++i) T[i]=read(),F[i]=read();    for (int i=n;i>=0;--i) sF[i]=sF[i+1]+F[i];    for (int i=0;i<=n;++i)    {        if (i) sT[i]=sT[i-1]+T[i];        q[i].id=i,q[i].k=-sT[i],q[i].x=sF[i+1];        p[i]=q[i];    }    sort(q+1,q+n+1,cmpx);    sort(p+1,p+n+1,cmpk);    memset(f,127,sizeof(f));f[0]=0;    cdq(0,n);    printf("%lld\n",f[n]);}
0 0