• BZOJ1096 [ZJOI2007]仓库建设 动态规划 斜率优化


    原文链接http://www.cnblogs.com/zhouzhendong/p/8696410.html

    题目传送门 - BZOJ1096

    题意

      给定两个序列$a,b,X$,现在划分$a$序列。

      被划分出来的段$[j,i]$的花费为$a_i+sum_{k=j+1}^{i}(X_i-X_k)b_k$。

      一种划分方式的花费就是每一段的花费之和。

      问最小花费。

      序列长度$leq 10^6$。

    题解

      这题是BZOJ3437的升级版(其实也没升多少……,不仅代码我是几乎原样复制到,连题解我几乎都是原样复制的……)

      首先我们不难写出DP方程:

      $$dp_i=max{dp_j+sum_{k=j+1}^{i}(X_i-X_k)b_k}+a_i (0leq j<i)$$

      然后容易看出可以斜率优化。

      然后搬出斜率优化的套路。

      先化简些式子。

      $$dp_j+sum_{k=j+1}^{i}(X_i-X_k)b_k\=dp[j]-sum_{k=j+1}^{i}X_kb_k+X_isum_{k=j+1}^{i}b_k$$

      设:

      $$sum_i=sum_{j=1}^{i}b_j$$

      $$vsum_i=sum_{j=1}^{i}b_j imes X_j$$

      则原式=

      $$dp_j-vsum_i+vsum_j+X_isum_i-X_isum_j$$

      然后开始斜率优化DP的套路部分:

      设(注意$x$的大小写)

      $$x_i=sum_i$$

      $$y_i=dp_i+vsum_i$$

      则原式=

      $$y_j-vsum_i+X_isum_i-X_ix_j$$

      假设$j>k$且从$j$转移不劣于$k$,则:

      $$y_j-vsum_i+X_isum_i-X_ix_jleq y_k-vsum_i+X_isum_i-X_ix_k$$

      化简得:

      $$frac{y_j-y_k}{x_j-x_k}leq X_i$$

      然后献上斜率优化DP套路:

      注意由于开始限制了$j>k$所以$x_j-x_k>0$,所以最后两边同时相除不等式仍然成立。

      设

      $$g_{i,j}=frac{y_i-y_j}{x_i-x_j} (i>j)$$

      则上式可以表示为$g_{j,k}<X_i$

      我们来发掘以下$g_{j,k}$的性质。

      1. 当$g_{j,k}leq X_i$时,由于随着$i$变大,$X_i$也变大,所以显然从$k$转移是永远不会比$j$好的,所以我们可以把$k$扔掉。

      2. 当$g_{i,j}leq g_{j,k}$时,从$i$或者$k$转移至少有一个不比$j$差,所以可以把$j$扔掉。为什么??

        若$g_{i,j}leq X_i$,显然$j$要被扔掉,根据第一个性质。

        若$g_{i,j}>X_i$,则$g_{j,k}>X_i$,那么显然$j$比$k$差,也得被扔掉。

      于是我们可以用一个单调队列来维护斜率的单调性。

      具体的:

      当情况1发生的时候让队首出队。

      在进队的时候,如果发生情况2,那么先让队尾出队,然后再进队。

      为了避免精度问题,我们可以把$x_i-x_j$乘上来。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    const int N=1000005;
    int n,q[N],head=1,tail=0;
    LL a[N],b[N],X[N],sum[N],vsum[N],dp[N],x[N],y[N];
    int main(){
    	scanf("%d",&n);
    	for (int i=1;i<=n;i++)
    		scanf("%lld%lld%lld",&X[i],&b[i],&a[i]);
    	for (int i=1;i<=n;i++)
    		sum[i]=sum[i-1]+b[i],vsum[i]=vsum[i-1]+b[i]*X[i];
    	q[++tail]=0;
    	for (int i=1;i<=n;i++){
    		int j=q[head+1],k=q[head];
    		while (tail-head>0&&y[j]-y[k]<=(x[j]-x[k])*X[i])
    			head++,j=q[head+1],k=q[head];
    		j=k;
    		dp[i]=dp[j]+vsum[j]-vsum[i]+(sum[i]-sum[j])*X[i]+a[i];
    		x[i]=sum[i];
    		y[i]=dp[i]+vsum[i];
    		j=q[tail],k=q[tail-1];
    		while (tail-head>0&&(y[i]-y[j])*(x[j]-x[k])<=(y[j]-y[k])*(x[i]-x[j]))
    			tail--,j=q[tail],k=q[tail-1];
    		q[++tail]=i;
    	}
    	printf("%lld",dp[n]);
    	return 0;
    }
    

      

  • 相关阅读:
    libevent源码学习之event
    游戏寻路A*算法
    游戏地图动态生成
    一个基于protocol buffer的RPC实现
    TCMalloc源码学习(四)(小内存块释放)
    TCMalloc源码学习(三)(小块内存分配)
    TCMalloc源码学习(二)
    第五十四篇 Linux相关——远程连接SSH
    第五十三篇 Linux相关——Web服务器
    第五十二篇 Linux相关——数据库服务MySQL
  • 原文地址:https://www.cnblogs.com/zhouzhendong/p/BZOJ1096.html
Copyright © 2020-2023  润新知