题目链接
假设有(3)段(a,b,c)
先切(ab)和先切(bc)的价值分别为
(a(b+c)+bc=ab+bc+ac)
((a+b)c+ab=ab+bc+ac)
归纳一下可以发现切的顺序并不影响总价值。
于是设(f[i][j])表示前(i)个数切(j)次的最大价值,转移方程就很简单了。
然后斜率优化一下就能降时间复杂度降到(O(nk))
(f[i][j]=f[k][j-1]+sum[k]*(sum[i]-sum[k]))
(f[k][j-1]-sum[k]^2=-sum[i]*sum[k]+f[i][j])
水分神器斜率优化
#include <cstdio>
#include <algorithm>
using namespace std;
const int MAXN = 100010;
const int MAXM = 205;
#define ll long long
#define re register
int n, m, p;
int q[MAXN], head, tail, sum[MAXN], fa[MAXN][MAXM];
ll f[MAXN][MAXM];
inline double k(int t, int i, int j){
if(sum[i] == sum[j]) return -2e18;
return (double)(f[i][t] - f[j][t] - (ll)sum[i] * sum[i] + (ll)sum[j] * sum[j]) / (sum[i] - sum[j]);
}
int main(){
scanf("%d%d", &n, &m);
for(re int i = 1; i <= n; ++i){
scanf("%d", &p);
sum[i] = sum[i - 1] + p;
}
for(re int j = 1; j <= m; ++j){
head = tail = 0;
for(re int i = 1; i <= n; ++i){
while(head < tail && k(j - 1, q[head], q[head + 1]) > -sum[i]) ++head;
re int l = q[head];
f[i][j] = f[l][j - 1] + (ll)sum[l] * (sum[i] - sum[l]);
fa[i][j] = l;
while(head < tail && k(j - 1, q[tail - 1], q[tail]) <= k(j - 1, q[tail], i)) --tail;
q[++tail] = i;
}
}
printf("%lld
", f[n][m]);
int now = fa[n][m];
while(m--){
printf("%d ", now);
now = fa[now][m];
}
return 0;
}