Luogu3195 [HNOI2008]玩具装箱TOY (方程变形 + 斜率优化 )
题意:
给出一个序列 {a[i]} 把其分成若干个区间,每个区间的价值为 W = (j − i + ∑ak(i<=k<=j) - L)2 ,求所有分割方案中价值之和的最小值。
细节:
仔细看数据范围: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] )2 <= f[j1] + ( a[i] - b[j1] )2
最后可得:f[j2]+ b[j2]2 -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\n", f[n]); return 0; }