一道非常好的斜率DP练习题from Z博士。

题面

(因为我懒所以我吃了latex,原题是有latex的)

【问题描述】

在遥远的新日暮里, 哲 ♂ 学家们正在潜心研究健美, 他们向工厂送去了
n 个订单, 要求工厂按顺序生产健美器材. 由于工厂只有一台机器, 等工厂开
始处理订单时, 订单已经全部过期了 QAQ. 工厂的机器每次启动时需要 T 的
时间, 而工厂每生产一批产品就需要关闭机器, 然后将这批产品送往新日暮里
(可视为瞬间送到). 每个产品生产所需时间为 t i , 每个订单的违约金为 x i , 同
时产品生产出来后每单位时间需要花费 w i 的费用储存。
对于每一批产品, 所需要支付的费用是完成这批产品的时间 * 这批产品
的总违约金 + 每个产品生产出来后等待送往新日暮里的时间 * 储存的费用.
你可以随意分批生产产品, 注意要按顺序生产产品.
老板并不想欠下 3.5 个亿, 更不想接受哲学的拷 ♂ 问. 他向你询问最小需要支付的费用是多少?

【输入格式】

从 factory.in 中读入数据。
输入文件第一行包括两个正整数 n, T , 分别表示订单的数量和机器启动
的时间。
接下来 n 行每行三个数 t i , x i , w i , 描述一个订单, 分别表示生产所需的时
间, 违约金和储存所需的费用。

【输出格式】

输出到文件 factory.out 中。
输出包括一个整数,表示最小需要支付的费用。

题解

设$f_i$表示从生产第$i$~$n$个产品所需要的最小费用。
根据题意可以得到DP方程:

$$f_i=\min_{j=i}^n(f_{j+1}+T(\sum_{k=i}^nx_k)+\sum_{k=1}^{j}\sum_{l=i}^jx_lt_k+\sum_{k=i}^{j}\sum_{l=k+1}^{j} w_k t_l)$$

然后可以提取出$T(\sum_{k=i}^nx_k)$得到:

$$f_i=T(\sum_{k=i}^nx_k)+\min_{j=i}^n(f_{j+1}+\sum_{k=1}^{j}\sum_{l=i}^jx_lt_k+\sum_{k=i}^{j}\sum_{l=k+1}^{j}w_kt_l)$$

接着我们发现 $\sum_{i=1}^{n}\sum_{j=1}^{u}x_it_j$ 是一定会进入答案的,则我们可以在转移方程里面去掉这一部分,然后发现可以做一次提公因式并把方程简化为:

$$f_i=T(\sum_{k=i}^nx_k)+\min_{j=i}^n(f_{j+1}+ \sum_{k=i}^j((x_k+w_k) \sum_{l=k+1}^{j}t_l))$$

并且最后的结果为

$$f_1+\sum_{i=1}^{n}\sum_{j=1}^{u}x_it_j$$

可是,,,这还是 $O(n^4)$ 的啊。。。
但我们可以用后缀和优化一波。
计算$x,w,t$的后缀和$X,W,T$,以及$z_i=(x_i+w_i)(T_{i+1})$的后缀和$Z$
则方程就会瞬间变为:
$$f_i=TX_k+min^n_{j=i}(f_{j+1}+Z_i-Z_{j+1}-(X_i+W_i-X_{j+1}-W_{j+1})T_{j+1})$$
这就是$O(n^2)$的方程了。
然后发现可以斜率优化!!!
一波$O(n)$妥妥AC。

/*
Author: CNYALI_LK
LANG: C++
PROG: factory.cpp
*/
#include<bits/stdc++.h>
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define all(x) x.begin(),x.end()
using namespace std;
const double eps=1e-8;
const double PI=acos(-1.0);
typedef unsigned long long ll;
template<class T>void chkmin(T &a,T b){a=a<b?a:b;}
template<class T>void chkmax(T &a,T b){a=a>b?a:b;}
template<class T>T sqr(T a){return a*a;}
template<class T>T mmin(T a,T b){return a<b?a:b;}
template<class T>T mmax(T a,T b){return a>b?a:b;}
template<class T>T aabs(T a){return a<0?-a:a;}
#define min mmin
#define max mmax
#define abs aabs
ll n,yT;
ll t[555555],x[555555],w[555555],z[555555],T[555555],X[555555],W[555555],Z[555555],f[555555];
#define x(a) (f[a]-Z[a]+(X[a]+W[a])*T[a])
#define y(a) T[a]
#define fst q[l]
#define ffst q[l+1]
#define lst q[r]
#define llst q[r-1]
#define nxt q[++r]
ll q[555555],l,r;
int main(){
    freopen("factory.in","r",stdin);
    freopen("factory.out","w",stdout);
    ll n,yT,ans=0;
    scanf("%llu%llu",&n,&yT);
    
    for(ll i=1;i<=n;++i){
        scanf("%llu%llu%llu",t+i,x+i,w+i);
    }
    for(ll i=1;i<=n;++i){
        T[i]=T[i-1]+t[i];
    }
    for(ll i=1;i<=n;++i)ans+=x[i]*T[i];
    for(ll i=n;i>=1;--i){
        z[i]=(x[i]+w[i])*(T[i+1]);
        X[i]=x[i]+X[i+1];
        W[i]=w[i]+W[i+1];
        Z[i]=z[i]+Z[i+1];
        T[i]=t[i]+T[i+1];
    }
    l=1;r=0;
    nxt=n+1;
    for(ll i=n;i>=1;--i){
        while(l<r&&(x(ffst)-x(fst))*1./(y(ffst)-y(fst))-(X[i]+W[i])<=eps)++l;
        ll j=q[l];
//      debug("%llu ",j);
        f[i]=X[i]*yT+f[j]+Z[i]-Z[j]-(X[i]+W[i]-X[j]-W[j])*T[j];
        while(l<r&&(x(i)-x(lst))*1./(y(i)-y(lst))-(x(i)-x(llst))*1./(y(i)-y(llst))<=eps)--r;
        nxt=i;
//      debug("%llu\n",f[i]);
    }
//  for(ll i=1;i<=n+1;++i)printf("%llu %llu %llu %llu %llu %llu %llu\n",x(i),y(i),f[i],Z[i],X[i],W[i],T[i]);
    ans+=f[1];
    printf("%llu\n",ans);
    return 0;
}

标签: 斜率DP

添加新评论