【题意分析】
题意就是把一个序列切k刀,每次的得分为序列两边元素之和的乘积
下证答案与切割的顺序无关,只与切割的位置有关。
//这是一个丑陋的图
假设你切在红色的两个位置,那么就有先切ab处再切bc处或者先切bc处再切ab处两种切法。
a n s 1 = a ( b + c ) + b c = a b + a c + b c ans1=a(b+c)+bc=ab+ac+bc ans1=a(b+c)+bc=ab+ac+bc
a n s 2 = ( a + b ) c + a b = a b + a c + b c ans2=(a+b)c+ab=ab+ac+bc ans2=(a+b)c+ab=ab+ac+bc
得证。
然后我们设状态dp[i][j]
为前i个切j刀的最大收益,那就枚举切的位置k
d p i , j = max 1 ≤ k < i { d p k , j − 1 + s u m k ∗ ( s u m i − s u m k ) } dp_{i,j}=\max_{1\leq k<i}\{dp_{k,j-1}+sum_k*(sum_i-sum_k)\} dpi,j=1≤k<imax{dpk,j−1+sumk∗(sumi−sumk)}
设两个决策x,y,x大于y且决策x比决策y更优
d p x + s u m x ∗ ( s u m i − s u m x ) > d p y + s u m y ∗ ( s u m i − s u m y ) dp_x+sum_x*(sum_i-sum_x)>dp_y+sum_y*(sum_i-sum_y) dpx+sumx∗(sumi−sumx)>dpy+sumy∗(sumi−sumy)
⇒ d p x − d p y + s u m y 2 − s u m x 2 s u m y − s u m x < s u m i \Rightarrow \frac{dp_x-dp_y+sum_y^2-sum_x^2}{sum_y-sum_x}<sum_i ⇒sumy−sumxdpx−dpy+sumy2−sumx2<sumi
然后直接斜率优化
注意0<=ai<=10^4,这意味着 s u m i sum_i sumi不一定递增, s u m x sum_x sumx可能等于 s u m y sum_y sumy,那么你就NaN了
所以slope的时候判一下相等return -1e18就好了
至于单调队列的等号条件,加与不加其实没有关系
内存512M可以不滚动(128M–的话就要滚动了),但滚动更好
Code:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <cctype>
#include <algorithm>
#define int long long
#define db long double
#define MAXN 100002
using namespace std;
int dp[MAXN][3], q[MAXN], path[MAXN][202], sum[MAXN], n, k;
inline int read () {
register int s = 0, w = 1;
register char ch = getchar ();
while (! isdigit (ch)) ch = getchar ();
while (isdigit (ch)) {s = (s << 3) + (s << 1) + (ch ^ 48); ch = getchar ();}
return s * w;
}
inline db slope (int x, int y, int o) {
if (sum[x] == sum[y]) return -1e18;
return 1.0 * (dp[x][o] - dp[y][o] + sum[y] * sum[y] - sum[x] * sum[x]) / (sum[y] - sum[x]);
}
signed main () {
n = read (), k = read ();
for (register int i = 1; i <= n; i++) sum[i] = sum[i - 1] + read ();
for (register int j = 1; j <= k; j++) {
int h = 0, t = 0; memset (q, 0, sizeof q);
int now = j & 1, pre = now ^ 1;
for (register int i = 1; i <= n; i++) {
while (h < t && slope (q[h + 1], q[h], pre) <= sum[i]) h++;
dp[i][now] = dp[q[h]][pre] + sum[q[h]] * (sum[i] - sum[q[h]]), path[i][j] = q[h];
while (h < t && slope (q[t], q[t - 1], pre) >= slope (i, q[t], pre)) t--;
q[++t] = i;
}
}
printf ("%lld\n", dp[n][k & 1]); int now = n;
for (register int i = k; i >= 1; now = path[now][i--]) printf ("%lld ", path[now][i]);
return 0;
}