Problem
【APIO2014】序列分割
T i m e L i m i t : 40 S e c \mathrm{Time\;Limit:\;40\;Sec} T i m e L i m i t : 4 0 S e c
M e m o r y L i m i t : 128 M B \mathrm{Memory\;Limit:\;128\;MB} M e m o r y L i m i t : 1 2 8 M B
Description
小 H \mathrm{小H} 小 H 最近迷上了一个分隔序列的游戏。
在这个游戏里,小 H \mathrm{小H} 小 H 需要将一个长度为n n n 的非负整数序列分割成k + 1 k+1 k + 1 个非空的子序列。
为了得到k + 1 k+1 k + 1 个子序列,小 H \mathrm{小H} 小 H 需要重复k k k 次以下的步骤:
小 H \mathrm{小H} 小 H 首先选择一个长度超过1 1 1 的序列(一开始小 H \mathrm{小H} 小 H 只有一个长度为n n n 的序列,也就是一开始得到的整个序列)
选择一个位置,并通过这个位置将这个序列分割成连续的两个非空的新序列
每次进行上述步骤之后,小 H \mathrm{小H} 小 H 将会得到一定的分数。这个分数为两个新序列中元素和的乘积。
小 H \mathrm{小H} 小 H 希望选择一种最佳的分割方式,使得k k k 轮之后,小 H \mathrm{小H} 小 H 的总得分最大。
输入第一行包含两个整数n , k ( k + 1 ≤ n ) n,k\;(k+1\le n) n , k ( k + 1 ≤ n ) 。
第二行包含n n n 个非负整数a 1 , a 2 , ⋯ , a n ( 0 ≤ a i ≤ 1 0 4 ) a_1,a_2,\cdots,a_n\;(0\le a_i\le10^4) a 1 , a 2 , ⋯ , a n ( 0 ≤ a i ≤ 1 0 4 ) ,表示一开始小 H \mathrm{小H} 小 H 得到的序列。
Output
输出第一行包含一个整数,为小 H \mathrm{小H} 小 H 可以得到的最大分数。
Sample Output
Hint
在样例中,小 H \mathrm{小H} 小 H 可以通过如下3轮操作得到108分:
一开始小 H \mathrm{小H} 小 H 有一个序列( 4 , 1 , 3 , 4 , 0 , 2 , 3 ) (4,1,3,4,0,2,3) ( 4 , 1 , 3 , 4 , 0 , 2 , 3 ) 。小 H \mathrm{小H} 小 H 选择在第1 1 1 个数之后的位置将序列分成两部分,并得到4 × ( 1 + 3 + 4 + 0 + 2 + 3 ) = 52 4\times (1+3+4+0+2+3)=52 4 × ( 1 + 3 + 4 + 0 + 2 + 3 ) = 5 2 分。
这一轮开始时小 H \mathrm{小H} 小 H 有两个序列:( 4 ) , ( 1 , 3 , 4 , 0 , 2 , 3 ) (4),\;(1,3,4,0,2,3) ( 4 ) , ( 1 , 3 , 4 , 0 , 2 , 3 ) 。小 H \mathrm{小H} 小 H 选择在第3个数字之后的位置将第二个序列分成两部分,并得到( 1 + 3 ) × ( 4 + 0 + 2 + 3 ) = 36 (1+3)\times(4+0+2+3)=36 ( 1 + 3 ) × ( 4 + 0 + 2 + 3 ) = 3 6 分。
这一轮开始时小 H \mathrm{小H} 小 H 有三个序列:( 4 ) , ( 1 , 3 ) , ( 4 , 0 , 2 , 3 ) (4),\;(1,3),\;(4,0,2,3) ( 4 ) , ( 1 , 3 ) , ( 4 , 0 , 2 , 3 ) 。小 H \mathrm{小H} 小 H 选择在第5 5 5 个数字之后的位置将第三个序列分成两部分,并得到( 4 + 0 ) × ( 2 + 3 ) = 20 (4+0)\times(2+3)=20 ( 4 + 0 ) × ( 2 + 3 ) = 2 0 分。
经过上述三轮操作,小 H \mathrm{小H} 小 H 将会得到四个子序列:( 4 ) , ( 1 , 3 ) , ( 4 , 0 ) , ( 2 , 3 ) (4),\;(1,3),\;(4,0),\;(2,3) ( 4 ) , ( 1 , 3 ) , ( 4 , 0 ) , ( 2 , 3 ) 并总共得到52 + 36 + 20 = 108 52+36+20=108 5 2 + 3 6 + 2 0 = 1 0 8 分。
标签:斜率优化DP
Solution
易得D P \mathrm{DP} D P 方程:f [ t ] [ i ] f[t][i] f [ t ] [ i ] 表示玩t t t 轮时只考虑[ 1 , i ] [1,i] [ 1 , i ] 区间内的数的最大贡献。那么有f [ t ] [ i ] = max j = 0 i − 1 { f [ t − 1 ] [ j ] + ( S i − S j ) × S j } f[t][i]=\max_{j=0}^{i-1}\lbrace{f[t-1][j]+(S_i-S_j)\times S_j}\rbrace f [ t ] [ i ] = max j = 0 i − 1 { f [ t − 1 ] [ j ] + ( S i − S j ) × S j } 。
简单推一推:
f [ t ] [ i ] = max j = 0 i − 1 { f [ t − 1 ] [ j ] + ( S i − S j ) × S j } = max j = 0 i − 1 { f [ t − 1 ] [ j ] + S j × S i − S j 2 } = max j = 0 i − 1 { S i S j + f [ t − 1 ] [ j ] − S j 2 } \begin{aligned}
f[t][i]&=\max_{j=0}^{i-1}\lbrace{f[t-1][j]+(S_i-S_j)\times S_j}\rbrace\\
&=\max_{j=0}^{i-1}\lbrace{f[t-1][j]+S_j\times S_i-S_j^2}\rbrace\\
&=\max_{j=0}^{i-1}\lbrace{S_iS_j+f[t-1][j]-S_j^2}\rbrace\\
\end{aligned}
f [ t ] [ i ] = j = 0 max i − 1 { f [ t − 1 ] [ j ] + ( S i − S j ) × S j } = j = 0 max i − 1 { f [ t − 1 ] [ j ] + S j × S i − S j 2 } = j = 0 max i − 1 { S i S j + f [ t − 1 ] [ j ] − S j 2 }
套上斜率优化:
F o r i ∈ [ 1 , n ] , i f p < q , q i s b e t t e r t h a n p , t h e n S p S i + f [ t − 1 ] [ p ] − S p 2 ≤ S q S i + f [ t − 1 ] [ q ] − S q 2 L e t a n = S n , b n = f [ t − 1 ] [ n ] − S n 2 , t h e n ( a p − a q ) S i ≤ b q − b p ∴ I f ( a p − a q ) S i ≤ b q − b p , t h e n w e c a n p o p p o u t o f t h e s t a c k . For\;i\in[1,n],\;if\;p<q,\;q\;is\;better\;than\;p,\;then\\
S_pS_i+f[t-1][p]-S_p^2\le S_qS_i+f[t-1][q]-S_q^2\\
Let\;a_n=S_n,\;b_n=f[t-1][n]-S_n^2,\\
then\;(a_p-a_q)S_i\le b_q-b_p\\
\therefore If\;(a_p-a_q)S_i\le b_q-b_p,\;then\;we\;can\;pop\;p\;out\;of\;the\;stack.
F o r i ∈ [ 1 , n ] , i f p < q , q i s b e t t e r t h a n p , t h e n S p S i + f [ t − 1 ] [ p ] − S p 2 ≤ S q S i + f [ t − 1 ] [ q ] − S q 2 L e t a n = S n , b n = f [ t − 1 ] [ n ] − S n 2 , t h e n ( a p − a q ) S i ≤ b q − b p ∴ I f ( a p − a q ) S i ≤ b q − b p , t h e n w e c a n p o p p o u t o f t h e s t a c k .
注意这里不要写成斜率的形式,因为a p − a q a_p-a_q a p − a q 可能等于0 0 0 。
按照不等式用单调栈对每层k k k 进行维护即可,这里可以做k k k 次一维D P \mathrm{DP} D P ,每次重新算b b b 数组。
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 #include <bits/stdc++.h> #define MAX_N 100000 using namespace std;typedef long long lnt;template <class T > inline void read (T &x) { x = 0 ; int c = getchar (), f = 1 ; for (; !isdigit (c); c = getchar ()) if (c == 45 ) f = -1 ; for (; isdigit (c); c = getchar ()) (x *= 10 ) += f*(c-'0' ); } int n, m, l, r, que[MAX_N+5 ]; lnt s[MAX_N+5 ];lnt f[MAX_N+5 ], a[MAX_N+5 ], b[MAX_N+5 ]; bool chk (int p, int l, int r) { lnt x = (b[p]-b[que[r]])*(a[que[r-1 ]]-a[que[r]]); lnt y = (b[que[r]]-b[que[r-1 ]])*(a[que[r]]-a[p]); return x <= y; } int main () { read (n), read (m); for (int i = 1 , x; i <= n; i++) read (x), s[i] = s[i-1 ]+x; for (int i = 1 ; i <= n; i++) a[i] = s[i], b[i] = -s[i]*s[i]; for (int k = 1 ; k <= m; k++) { que[l = r = 0 ] = 0 ; for (int i = 1 ; i <= n; i++) { while (l < r && s[i]*(a[que[l]]-a[que[l+1 ]]) <= b[que[l+1 ]]-b[que[l]]) l++; f[i] = a[que[l]]*s[i]+b[que[l]]; while (l < r && chk (i, l, r)) r--; que[++r] = i; } for (int i = 1 ; i <= n; i++) b[i] = f[i]-s[i]*s[i]; } return printf ("%lld\n" , f[n]), 0 ; }