P3195 [HNOI2008] 玩具装箱
//URL: https://www.luogu.com.cn/problem/P3195 /* P3195 [HNOI2008] 玩具装箱 斜率DP P 教授要去看奥运,但是他舍不下他的玩具,于是他决定把所有的玩具运到北京。他使用自己的压缩器进行压缩,其可以将任意物品变成一堆,再放到一种特殊的一维容器中。 P 教授有编号为 1⋯n1⋯n 的 nn 件玩具,第 ii 件玩具经过压缩后的一维长度为 CiCi。 为了方便整理,P教授要求: 在一个一维容器中的玩具编号是连续的。 同时如果一个一维容器中有多个玩具,那么两件玩具之间要加入一个单位长度的填充物。形式地说,如果将第 ii 件玩具到第 jj 个玩具放到一个容器中,那么容器的长度将为 x=j−i+∑k=ijCkx=j−i+k=i∑jCk。 制作容器的费用与容器的长度有关,根据教授研究,如果容器长度为 xx,其制作费用为 (x−L)2(x−L)2。其中 LL 是一个常量。P 教授不关心容器的数目,他可以制作出任意长度的容器,甚至超过 LL。但他希望所有容器的总费用最小 dp[i]:前i个分成若干组最小值 dp[i]=min(dp[j]+(sum[i]-sum[j]+i-(j+1)-L)^2) (j<i) O(N^2) a[i]=sum[i]+i,b[i]=sum[i]+i+L+1 dp[i]=dp[j]+a[i]^2-2*a[i]*b[j]+b[j]^2 ->2*a[i]*b[j]+dp[i]-a[i]^2=dp[j]+b[j]^2 K*X+B=Y */ /* */ #include<cstdio> #include<iostream> #include<algorithm> #include<cmath> #include<string.h> #include<queue> #include<vector> #include<bits/stdc++.h> typedef long long ll; #define ddd printf("-----------------------\n"); using namespace std; const int maxn=5e4 +10; const int mod=998244353; const int inf=0x3f3f3f3f; ll N, L, dp[maxn], sum[maxn], f[maxn], g[maxn],q[maxn]; double xie(int j1, int j2) { return (double) (dp[j2] + g[j2] - dp[j1] - g[j1]) / (f[j2] - f[j1]); } int main() { ios::sync_with_stdio(false); scanf("%d%d", &N, &L); for(int i = 1; i <= N; i++) { scanf("%d", &sum[i]); sum[i] += sum[i - 1]; f[i] = sum[i] + i; g[i] = (f[i] + L + 1) * (f[i] + L + 1); } g[0] = (L + 1) * (L + 1); //!!!!!! int head=1,tail=0;q[++tail]=0; for(int i = 1; i <= N; i++) { while(head < tail && xie(q[head], q[head + 1]) <= 2 * f[i]) head++; dp[i] = dp[q[head]] + (f[i] - f[q[head]] - L - 1) * (f[i] - f[q[head]] - L - 1); //更新dp值 while(head < tail && xie(q[tail], i) < xie(q[tail - 1], q[tail])) tail--; q[++tail] = i; } printf("%lld\n", dp[N]); return 0; }