题解「CSP2019 划分」
设 \(f_i\) 为 \(1\sim n\) 划分成若干段得到的最小值,\(g_i\) 为 \(f_i\) 转移过来的决策值。
什么意思呢?我们有 \(\text{DP}\) 转移方程:
\[f_i=\min\{f_j+(sum_i-sum_j)^2\}(g_j\leq sum_i-sum_j)
\]
那么 \(g_i\) 就为令上式取到最小值的 \(sum_i-sum_j\)。
这样就有了一个 \(O(n^2)\) 的做法,能够获得 \(64\text{pts}\)。
我们发现上式的约束可以改写为 \(g_j+sum_j\leq sum_i\),又发现 \(sum_i\) 是单增的,这使我们想到用单调队列维护。正确性基于,最后一段我们希望它尽可能的小。于是时间复杂度优化为 \(O(n)\)。
只能获得 \(88\text{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\n",f[n]);
return 0;
}