Luogu3195 [HNOI2008]玩具装箱TOY (方程变形 + 斜率优化 )

题意:

  给出一个序列 {a[i]} 把其分成若干个区间,每个区间的价值为 W = (− i + ∑ak(i<=k<=j) - L),求所有分割方案中价值之和的最小值。

细节:

  仔细看数据范围:1 <= N <= 50000 ,1 <= C[i] <= 10^7,所以不忘记开Long Long / Int64,咳咳我很关心 Pascal 选手。

分析:

  显然一题划分区间求最值的题目,所以可以用动态规划解决,联想一下状态:f[i] 表示以 i 为结尾且以 i 为断点的最小价值和。

  考虑到 i 是断点的问题所以对于后面的状态,新的一段区间是不包括 i 的,所以我们可以开始转移了:f[i] = min { f[j] + (sum[i] - sum[j] + i - (j + 1) - L)2 }

  得到了一个 1D/1D 的动规方程,对于这一类式子我们一般考虑优化转移,显然状态无法进行优化,不然思想转变成了贪心,显然局部最有不满足全局最有,我们可以尝试是否存在在转移时的无用状态,所谓无用状态是一些不优的状态,我们显然可以选择最有的状态进行转移,其满足最有字结构的性质。

  不妨令 j1 < j2 < i,且 j1 的转移不必 j2 优,从中我们可以发现不需要去求解 j1 直接从 j2 进行转移不会影响答案,且能够节省时间。

  所以我们可以得到式子 f[j2] + (sum[i] - sum[j2] + i - (j2 + 1) - L)2 <= f[j1] + (sum[i] - sum[j1] + i - (j1 + 1) - L)2,由于式子过于复杂我们考虑换元。

  不妨再令 a[i] = sum[i] - L + i - 1,b[i] = sum[i] + i

  所以式子转换成:f[j2] + ( a[i] - b[j2] )<= f[j1] + ( a[i] - b[j1] )2

  最后可得:f[j2]+ b[j2]-f[j1]-b[j1]2 <= 2 × a[i] ×( b[j2] - b[j1] ) 所以此时我们可以考虑斜率优化具体的分析请详见一下本人博客

代码:

本人压行怪物请见谅:

#include<bits/stdc++.h>
#define MAXN 50010
#define LL long long
using namespace std;

LL f[MAXN], sum[MAXN], a[MAXN], b[MAXN];
int n, L, que[MAXN];

int main(){
    scanf("%d%d", &n, &L);
    for (int i=1; i<=n; i++){
        LL x;
        scanf("%lld", &x);
        sum[i]=sum[i-1]+x;
    }
    for (int i=1; i<=n; i++) a[i]=sum[i]+i-1-L, b[i]=sum[i]+i;
    int head=1, tail=1;
    for (int i=1; i<=n; i++){
        while (head<tail && 2*a[i]*(b[que[head+1]]-b[que[head]])>=f[que[head+1]]-f[que[head]]+b[que[head+1]]*b[que[head+1]]-b[que[head]]*b[que[head]]) ++head;
        f[i]=f[que[head]]+(a[i]-b[que[head]])*(a[i]-b[que[head]]);
        while (head<tail && (f[que[tail]]+b[que[tail]]*b[que[tail]]-f[que[tail-1]]-b[que[tail-1]]*b[que[tail-1]])*(b[i]-b[que[tail]])>=(f[i]+b[i]*b[i]-f[que[tail]]-b[que[tail]]*b[que[tail]])*(b[que[tail]]-b[que[tail-1]])) --tail;
        que[++tail]=i;
    }
    printf("%lld
", f[n]);
    return 0;
}
原文地址:https://www.cnblogs.com/xiannvzuimei/p/9987631.html