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

题目描述

传送门

题解

哈哈哈速度实力倒数第一
也不知为什么写的奇慢无比卡常数卡到丧心病狂才勉强过了
我分明是把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
#include
#include
#include
#include
using namespace std;
#define LL long long
#define N 1005

int 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
                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
#include
#include
#include
#include
using namespace std;
#define LL long long
#define N 300005

int 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;jif (f[j]+sT[i]*sF[j+1]+s*sF[j+1]1]+s*sF[j+1];
        f[i]-=sT[i]*sF[i+1];
    }
    printf("%I64d\n",f[n]);
}

O(nlogn)

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

const 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.xx||(a.x==b.x&&a.y>b.y);
}
int cmpk(data a,data b)
{
    return a.kif (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 (nowq[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 (Minq(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].kelse 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]);
}

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