• BZOJ3437 小P的牧场 动态规划 斜率优化


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

    题目传送门 - BZOJ3437

    题意

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

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

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

      问最小花费。

      序列长度$leq 10^6$。

    题解

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

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

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

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

      先化简些式子。

      $$dp_j+sum_{k=j+1}^{i}(i-k)b_k\=dp[j]-sum_{k=j+1}^{i}kb_k+isum_{k=j+1}^{i}b_k$$

      设:

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

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

      则原式=

      $$dp_j-vsum_i+vsum_j+isum_i-isum_j$$

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

      设

      $$x_i=sum_i$$

      $$y_i=dp_i+vsum_i$$

      则原式=

      $$y_j-vsum_i+isum_i-ix_j$$

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

      $$y_j-vsum_i+isum_i-ix_jleq y_k-vsum_i+isum_i-ix_k$$

      化简得:

      $$frac{y_j-y_k}{x_j-x_k}leq 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}<i$

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

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

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

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

        若$g_{i,j}>i$,则$g_{j,k}>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],sum[N],vsum[N],dp[N],x[N],y[N];
    int main(){
    	scanf("%d",&n);
    	for (int i=1;i<=n;i++)
    		scanf("%lld",&a[i]);
    	for (int i=1;i<=n;i++){
    		scanf("%lld",&b[i]);
    		sum[i]=sum[i-1]+b[i];
    		vsum[i]=vsum[i-1]+b[i]*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])*i)
    			head++,j=q[head+1],k=q[head];
    		j=k;
    		dp[i]=dp[j]+vsum[j]-vsum[i]+(sum[i]-sum[j])*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;
    }
    

      

  • 相关阅读:
    数据库表结构变动发邮件脚本
    .net程序打包部署
    无法登陆GitHub解决方法
    netbeans 打包生成 jar
    第一次值班
    RHEL6 纯命令行文本界面下安装桌面
    C语言中格式化输出,四舍五入类型问题
    I'm up to my ears
    How to boot ubuntu in text mode instead of graphical(X) mode
    the IP routing table under linux@school
  • 原文地址:https://www.cnblogs.com/zhouzhendong/p/BZOJ3437.html
Copyright © 2020-2023  润新知