bzoj 3675 [Apio2014]序列分割

假设序列总和为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 }
View Code

 

posted @ 2016-04-29 18:48  assassain  阅读(167)  评论(0编辑  收藏  举报