题解「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;
}
posted @ 2020-11-30 23:01  tommymio  阅读(61)  评论(0编辑  收藏  举报