设 (f_i) 为 (1sim n) 划分成若干段得到的最小值,(g_i) 为 (f_i) 转移过来的决策值。
什么意思呢?我们有 ( ext{DP}) 转移方程:
[f_i=min{f_j+(sum_i-sum_j)^2}(g_jleq sum_i-sum_j)
]
那么 (g_i) 就为令上式取到最小值的 (sum_i-sum_j)。
这样就有了一个 (O(n^2)) 的做法,能够获得 (64 ext{pts})。
我们发现上式的约束可以改写为 (g_j+sum_jleq sum_i),又发现 (sum_i) 是单增的,这使我们想到用单调队列维护。正确性基于,最后一段我们希望它尽可能的小。于是时间复杂度优化为 (O(n))。
只能获得 (88 ext{pts}),由于没有高精/kk
#include<cstdio>
#define int ll
typedef long long ll;
int f[1000005],g[1000005],a[1000005],q[1000005];
inline int read() {
register int x=0,f=1;register char s=getchar();
while(s>'9'||s<'0') {if(s=='-') f=-1;s=getchar();}
while(s>='0'&&s<='9') {x=x*10+s-'0';s=getchar();}
return x*f;
}
signed main() {
int n=read(),type=read();
for(register int i=1;i<=n;++i) a[i]=a[i-1]+read();
int hd=0,tl=0;
for(register int i=1;i<=n;++i) {
while(hd<tl&&a[q[hd+1]]+g[q[hd+1]]<=a[i]) ++hd;//g[q[hd+1]]<=a[i]-a[q[hd+1]]
g[i]=a[i]-a[q[hd]]; f[i]=f[q[hd]]+g[i]*1ll*g[i];
while(hd<=tl&&a[q[tl]]+g[q[tl]]>=a[i]+g[i]) --tl;
q[++tl]=i;
}
printf("%lld
",f[n]);
return 0;
}