【题解】
一个重要的结论:
对于同一组分割方式,总得分与分割的先后顺序无关
不妨考虑最先分成的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; }