洛咕
(M+1)块,每一次选择一个有超过一个元素的块(初始时你只有一块,即整个序列)选择两个相邻元素把这个块从中间分开,得到两个非空的块.每次操作后你将获得那两个新产生的块的元素和的乘积的分数.求最大总得分,并输出任意一种方案.
分析:首先我们猜一个结论,就是对于一个序列,我们割的顺序不影响最后的得分.
(a*(b+c)+b*c=(a+b)*c+a*b=ab+ac+bc).证毕.
(f[n][m+1]),则有
(f[i][j]=f[k][j-1]+(sum[i]-sum[k])*(sum[n]-sum[i]))其中(k<i).
(l<k)且k比l更优,即
(f[k][j-1]+(sum[i]-sum[k])*(sum[n]-sum[i])>f[l][j-1]+(sum[i]-sum[l])*(sum[n]-sum[i]))
整理一下这个式子得到,
(frac{f[k][j-1]-f[l][j-1]}{sum[k]-sum[l]}>sum[n]-sum[i])
就可以愉快地斜率优化了.
(1ll)就好了.
(pre[n][m+1])往前找就行了.
#include<bits/stdc++.h>
#define N 100005
#define M 205
#define LL long long
#define rg register
using namespace std;
inline int read(){
int s=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){s=s*10+ch-'0';ch=getchar();}
return s*w;
}
int q[N],pre[N][M],sum[N];LL f[N][2];
int main(){
int n=read(),m=read();
for(rg int i=1;i<=n;++i)sum[i]=read(),sum[i]+=sum[i-1];
for(rg int j=1;j<=m+1;++j){
rg int l=1,r=1;q[1]=0;
for(rg int i=1;i<=n;++i){
while(l<r&&(f[q[l+1]][(j-1)&1]-f[q[l]][(j-1)&1])>=(1ll*(sum[n]-sum[i])*(sum[q[l+1]]-sum[q[l]])))l++;
f[i][j&1]=f[q[l]][(j-1)&1]+1ll*(sum[i]-sum[q[l]])*(sum[n]-sum[i]);
pre[i][j]=q[l];
while(l<r&&(1ll*(f[q[r]][(j-1)&1]-f[q[r-1]][(j-1)&1])*(sum[i]-sum[q[r]]))<=(1ll*(f[i][(j-1)&1]-f[q[r]][(j-1)&1])*(sum[q[r]]-sum[q[r-1]])))r--;
q[++r]=i;
}
}
printf("%lld
",f[n][(m+1)&1]);
for(rg int i=m+1;i>=2;--i){
printf("%d ",pre[n][i]);
n=pre[n][i];
}
return 0;
}