【题解】
一个重要的结论:
对于同一组分割方式,总得分与分割的先后顺序无关
不妨考虑最先分成的3部分,设区间和分别为Sa,Sb,Sc
可以证明,先分割a,b还是b,c,最终得分都是ab+bc+ca,即最先分成的3部分无需考虑顺序,子问题也是一样
于是,从前往后切割即可
设f[x][i]为前i个数分x份的最大得分,显然1<=x<=k+1,x<=i,S[i]为前缀和
dp方程:f[x][i]=max{ f[x-1][j] + S[j]*(S[i]-S[j]) },
设对于i,j比k优,代入上式并拆开
设y[j]=S[j]^2-f[x-1][j]
可以转化为维护斜率为:(y[j]-y[k])/(S[j]-S[k])的下凸壳
显然第一维可以状压成两行,不难处理
注意:a[i]可能为0,那么斜率的分母也可能为0
用这个方法避免复杂判断:若分母为0,根据y[j]-y[k]的正负返回 INF,0或-INF
这个略坑啊。
【代码】
#include<stdio.h>
#include<stdlib.h>
#define INF 100000000000000000.0
typedef long long LL;
LL s[100005],f[2][100005],y[100005];
int q[100005];
double K(int a,int b)
{
LL dy=y[a]-y[b],dx=s[a]-s[b];
if(dx==0)//注意分母可能为0
{
if(dy>0) return INF;
if(dy==0) return 0;
return -INF;
}
return (double)dy/(double)dx;
}
int main()
{
int n,k,x,i,head,tail;
scanf("%d%d",&n,&k);
k++;
for(i=1;i<=n;i++)
{
scanf("%lld",&s[i]);
s[i]+=s[i-1];
}
for(x=2;x<=k;x++)
{
head=0;
tail=1;
q[0]=x-1;
y[x-1]=s[x-1]*s[x-1]-f[x-1&1][x-1];
for(i=x;i<=n;i++)
{
while( tail-head>1 && K(q[head],q[head+1]) < s[i] ) head++;
f[x&1][i]=f[x-1&1][q[head]]+s[q[head]]*(s[i]-s[q[head]]);
y[i]=s[i]*s[i]-f[x-1&1][i];
while( tail-head>1 && K(q[tail-2],q[tail-1]) > K(q[tail-1],i) ) tail--;
q[tail++]=i;
}
}
printf("%lld",f[k&1][n]);
return 0;
}