首先分的顺序不影响最后的总得分。比如说三块内部的总得分是 (a,b,c),(c(a+b)+ab=a(b+c)+bc=ab+ac+bc),显然每次分三块讨论可以适用于所有情况。
所以可以令 (f_{i,l}) 表示前 (i) 个元素分了 (l) 个块的最大总得分。对 (a) 做一遍前缀和。
转移方程也很好写:
[f_{i,l}=min{f_{j,l-1}+a_j imes(a_i-a_j)|l-1leq j<i}
]
考虑两个决策 (j,k(j<k)),若 (j) 比 (k) 优:
[f_{j,l-1}+a_j imes(a_i-a_j)>f_{k,l-1}+a_k imes(a_i-a_k)
]
[f_{j,l-1}-f_{k,l-1}>a_k imes a_i-a_k^2-a_j imes a_i+a_j^2
]
[f_{j,l-1}-a_j^2-f_{k,l-1}+a_k^2>(a_k-a_j) imes a_i
]
[dfrac{f_{j,l-1}-a_j^2-f_{k,l-1}+a_k^2}{a_k-a_j}>a_i
]
因为 (a_i) 随着 (i) 的增大单调不减,所以对 ((-a_i,f_{i,l-1}-a_i^2)) 维护一个下凸包。
时间复杂度 (Oleft(nk ight))。
code:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define N 100005
#define Db double
#define Min(x,y)((x)<(y)?x:y)
#define For(i,x,y)for(i=x;i<=(y);i++)
#define Down(i,x,y)for(i=x;i>=(y);i--)
ll g[N],f[N];
int pre[N][205],a[N],pos[N],que[N];
Db slope(int j,int k)
{
return Db(g[j]-1LL*a[j]*a[j]-g[k]+1LL*a[k]*a[k])/Db(a[k]-a[j]);
}
int main()
{
int n=0,m,i,head,tail,l,k;
scanf("%d%d",&m,&k);
For(i,1,m)
{
scanf("%d",&a[n+1]);
if(a[n+1])
{
n++;
a[n]+=a[n-1];
pos[n]=i;
}
}
k=Min(k,n);
For(l,1,k)
{
head=tail=1;
For(i,1,n)
{
while(head<tail&&slope(que[head],que[head+1])<=a[i])head++;
pre[i][l]=que[head];
f[i]=g[que[head]]+1LL*a[que[head]]*(a[i]-a[que[head]]);
while(head<tail&&slope(que[tail-1],i)>=slope(que[tail],i))tail--;
que[++tail]=i;
}
For(i,1,n)g[i]=f[i];
}
printf("%lld
",f[n]);
i=n;
Down(l,k,1)printf("%d ",pos[pre[i][l]]),i=pre[i][l];
return 0;
}