zoukankan      html  css  js  c++  java
  • BZOJ1010 [HNOI2008]玩具装箱toy 动态规划 斜率优化

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

    题目传送门 - BZOJ1010

    题意

      一个数列$C$,然后把这个数列划分成若干段。

      对于数列$C$的某一段,是从$i$~$j$的,那么就会产生$(i-j+(sum_{k=i}^j C_k)-L)^2$的花费。

      一种划分方式的花费就是划分出来的每一段产生的花费和。

      求所有不同的划分方式所产生的总花费中最小花费为多少。

      序列长度$leq 5 imes 10^4$。

    题解

      看着好像斜率优化啊。

      恩对斜率优化,我们来推式子。

      记

      $dp_i$表示数列$C$的长度为$i$的前缀序列的最小花费。

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

      $s_i=sum_i+i$

      于是我们很容易得到:

      $$dp_i=min{dp_j+(s_i-s_j-1-L)^2}(0leq j<i)$$

      然后我们推一推式子。

      $$dp_j+(s_i-s_j-1-L)^2\=dp_j+s_j^2+2(L+1)s_j-2s_is_j+si^2-2(L+1)s_i+(L+1)^2$$

      假设$j>k$,且选$j$优于选择$k$,则:

      $$dp_j+s_j^2+2(L+1)s_j-2s_is_j+si^2-2(L+1)s_i+(L+1)^2<dp_k+s_k^2+2(L+1)s_k-2s_is_k+si^2-2(L+1)s_i+(L+1)^2$$

      $$Longrightarrow dp_j+s_j^2+2(L+1)s_j-2s_is_j<dp_k+s_k^2+2(L+1)s_k-2s_is_k$$

      令

      $$x_i=s_i$$

      $$y_i=dp_i+s_i^2+2(L+1)s_j$$

      $$dp_j+s_j^2+2(L+1)s_j-2s_is_j<dp_k+s_k^2+2(L+1)s_k-2s_is_k$$

      $$Longrightarrow y_j-2s_ix_j<y_k-2s_ix_k$$

      $$Longrightarrow frac{y_j-y_k}{x_j-x_k}<2s_i$$

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

      设

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

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

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

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

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

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

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

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

      具体的:

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

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

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

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    const int N=50005;
    int n,q[N],head=1,tail=0;
    LL L,s[N],dp[N],x[N],y[N];
    int main(){
    	scanf("%d%lld",&n,&L);
    	for (int i=1;i<=n;i++)
    		scanf("%lld",&s[i]),s[i]+=s[i-1]+1;
    	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]<=2LL*s[i]*(x[j]-x[k]))
    			head++,j=q[head+1],k=q[head];
    		j=k;
    		dp[i]=dp[j]+(s[i]-s[j]-L-1)*(s[i]-s[j]-L-1);
    		x[i]=s[i];
    		y[i]=dp[i]+s[i]*s[i]+2LL*(L+1)*s[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;
    }
    

      

  • 相关阅读:
    预备知识
    开场白
    H.264 / MPEG-4 Part 10 White Paper-翻译
    H.264简介
    batchGetAnchorLevel(dubbo接口)
    【Python022--递归】
    【python021-函数lambda表达式】
    【Python020--内嵌函数和闭包】
    【Python019--函数与过程】
    【python018--函数参数】
  • 原文地址:https://www.cnblogs.com/zhouzhendong/p/BZOJ1010.html
Copyright © 2011-2022 走看看