假设序列总和为tot, 每一段序列的和为 sum[i]
那么每一段序列,都被乘了(tot-sum[i]),将所有序列的贡献累加起来再除以2就是答案
考虑f[i][j]为前i个数,分了j块的最优值
那么 f[i][j] = max(f[k][j-1]+(sum[i]-sum[k])*(tot-(sum[i]-sum[k]))) k ∈ [j-1, i-1];
现在我们要优化这个转移 考虑如果从 k 转移比从 l 转移更优,那么可以化简式子 (f[k][j-1]-f[l][j-1])/(sum[k]-sum[l])-sum[k]-sum[l]>tot-2*sum[i]
然后就可以斜率优化了,时间复杂度 O(Kn)
1 #define MAXN 100010UL 2 #include <cstdio> 3 #define eps 1e-7 4 5 using namespace std; 6 7 typedef long long ll; 8 9 int n, hd, tl, nw, lt = 1, K, q[MAXN]; 10 ll sum[MAXN], f[MAXN][2]; 11 12 ll Get_(ll x) { 13 return x*(sum[n]-x); 14 } 15 16 double Get_k(int i, int j) { 17 if(sum[i]==sum[j]) return 0; 18 return (double)(f[i][lt]-f[j][lt])/(double)(sum[i]-sum[j])-sum[i]-sum[j]; 19 } 20 21 bool Check(int i, int j, int k) { 22 return Get_k(i, j)-Get_k(j, k)>-eps; 23 } 24 25 int main() { 26 scanf("%d%d", &n, &K); 27 ++ K; 28 for(int i = 1 ; i <= n ; ++ i) scanf("%lld", &sum[i]), sum[i] += sum[i-1]; 29 for(int i = 1 ; i <= n ; ++ i) f[i][0] = sum[i]*(sum[n]-sum[i]); 30 for(int k = 2 ; k <= K ; ++ k) { 31 nw ^= 1, lt ^= 1; 32 hd = tl = 0; 33 q[tl ++] = k-1; 34 for(int i = k ; i <= n ; ++ i) { 35 while(hd+1<tl&&f[q[hd+1]][lt]+Get_(sum[i]-sum[q[hd+1]])>=f[q[hd]][lt]+Get_(sum[i]-sum[q[hd]])) ++ hd; 36 f[i][nw] = f[q[hd]][lt]+Get_(sum[i]-sum[q[hd]]); 37 while(hd+1<tl&&Check(i, q[tl-1], q[tl-2])) -- tl; 38 q[tl ++] = i; 39 } 40 } 41 printf("%lld", f[n][nw]/2); 42 return 0; 43 }