题目描述
你正在玩一个关于长度为
n
n
n 的非负整数序列的游戏。这个游戏中你需要把序列分成
k
+
1
k + 1
k+1 个非空的块。为了得到
k
+
1
k+1
k+1 块,你需要重复下面的操作
k
k
k 次:
选择一个有超过一个元素的块(初始时你只有一块,即整个序列)
选择两个相邻元素把这个块从中间分开,得到两个非空的块。
每次操作后你将获得那两个新产生的块的元素和的乘积的分数。你想要最大化最后的总得分。
输入输出格式
输入格式:
第一行包含两个整数
n
n
n 和
k
k
k。保证
k
+
1
≤
n
k+1≤n
k+1≤n。
第二行包含 n n n 个非负整数 a 1 , a 2 , ⋯   , a n a_1, a_2, \cdots, a_n a1,a2,⋯,an ( 0 ≤ a i ≤ 1 0 4 ) (0≤a_i≤10^4) (0≤ai≤104),表示前文所述的序列。
输出格式:
第一行输出你能获得的最大总得分。
第二行输出
k
k
k 个介于
1
1
1 到
n
−
1
n−1
n−1 之间的整数,表示为了使得总得分最大,你每次操作中分开两个块的位置。第
i
i
i 个整数
s
i
s_i
si 表示第
i
i
i 次操作将在
s
i
s_i
si和
s
i
+
1
s_{i + 1}
si+1之间把块分开。
如果有多种方案使得总得分最大,输出任意一种方案即可。
输入输出样例
输入样例#1:
7 3
4 1 3 4 0 2 3
输出样例#1:
108
1 3 5
说明
你可以通过下面这些操作获得 108 108 108 分:
初始时你有一块 ( 4 , 1 , 3 , 4 , 0 , 2 , 3 ) (4, 1, 3, 4, 0, 2, 3) (4,1,3,4,0,2,3)。在第 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)=52分。
你现在有两块 ( 4 ) , ( 1 , 3 , 4 , 0 , 2 , 3 ) (4), (1, 3, 4, 0, 2, 3) (4),(1,3,4,0,2,3)。在第 3 3 3 个元素后面分开,获得 ( 1 + 3 ) × ( 4 + 0 + 2 + 3 ) = 36 (1 + 3) \times (4 + 0 + 2 + 3) = 36 (1+3)×(4+0+2+3)=36分。
你现在有三块 ( 4 ) , ( 1 , 3 ) , ( 4 , 0 , 2 , 3 ) (4), (1, 3), (4, 0, 2, 3) (4),(1,3),(4,0,2,3)。在第 5 5 5 个元素后面分开,获得 ( 4 + 0 ) × ( 2 + 3 ) = 20 (4 + 0) \times (2 + 3) = 20 (4+0)×(2+3)=20 分。
所以,经过这些操作后你可以获得四块 ( 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 52+36+20=108 分。
限制与约定
第一个子任务共 11 分,满足 1 ≤ k < n ≤ 10 1≤k<n≤10 1≤k<n≤10。
第二个子任务共 11 分,满足 1 ≤ k < n ≤ 50 1≤k<n≤50 1≤k<n≤50。
第三个子任务共 11 分,满足 1 ≤ k < n ≤ 200 1≤k<n≤200 1≤k<n≤200。
第四个子任务共 17 分,满足 2 ≤ n ≤ 1000 , 1 ≤ k ≤ m i n ( n − 1 , 200 ) 2≤n≤1000,1≤k≤min(n−1,200) 2≤n≤1000,1≤k≤min(n−1,200)。
第五个子任务共 21 分,满足 2 ≤ n ≤ 10000 , 1 ≤ k ≤ m i n ( n − 1 , 200 ) 2≤n≤10000,1≤k≤min(n−1,200) 2≤n≤10000,1≤k≤min(n−1,200)。
第六个子任务共 29 分,满足 2 ≤ n ≤ 100000 , 1 ≤ k ≤ m i n ( n − 1 , 200 ) 2≤n≤100000,1≤k≤min(n−1,200) 2≤n≤100000,1≤k≤min(n−1,200)。
分析:
对于相同位置的切法,不同的顺序切不影响答案。
显然
(
a
+
b
)
c
+
a
b
=
a
(
b
+
c
)
+
b
c
(a+b)c+ab=a(b+c)+bc
(a+b)c+ab=a(b+c)+bc。
设
f
[
i
]
[
j
]
f[i][j]
f[i][j]为前
i
i
i个位置切了
j
j
j块的答案。
有
f
[
i
]
[
j
]
=
max
k
=
0
i
−
1
f
[
k
]
[
j
−
1
]
+
s
u
m
[
k
]
∗
(
s
u
m
[
i
]
−
s
u
m
[
k
]
)
f[i][j]=\max_{k=0}^{i-1} f[k][j-1]+sum[k]*(sum[i]-sum[k])
f[i][j]=maxk=0i−1f[k][j−1]+sum[k]∗(sum[i]−sum[k])
即
s
u
m
[
k
]
2
−
f
[
k
]
[
j
−
1
]
=
s
u
m
[
i
]
∗
s
u
m
[
k
]
−
f
[
i
]
[
j
]
sum[k]^2-f[k][j-1]=sum[i]*sum[k]-f[i][j]
sum[k]2−f[k][j−1]=sum[i]∗sum[k]−f[i][j]
那么每个状态表示成
(
s
u
m
[
k
]
,
s
u
m
[
k
]
2
−
f
[
k
]
[
j
−
1
]
)
(sum[k],sum[k]^2-f[k][j-1])
(sum[k],sum[k]2−f[k][j−1]),然后维护凸壳即可。
因为斜率单调递增,可以斜率优化。
注意
s
u
m
[
i
]
sum[i]
sum[i]可能等于
s
u
m
[
j
]
sum[j]
sum[j],此时斜率可以记为inf。
代码:
// luogu-judger-enable-o2
#include <iostream>
#include <cstdio>
#include <cmath>
#define LL long long
#define LB long double
const int maxn=1e5+7;
const LL inf=1e13;
using namespace std;
int n,m,h,t,g,cnt;
int sum[maxn],q[maxn],fa[maxn][203],ans[maxn];
LL f[maxn][2];
LB get(int a,int b)
{
if (sum[a]==sum[b]) return inf;
LB deltay=(LB)sum[a]*(LB)sum[a]-(LB)sum[b]*(LB)sum[b]-f[a][g^1]+f[b][g^1];
LB deltax=(LB)sum[a]-(LB)sum[b];
return deltay/deltax;
}
int main()
{
scanf("%d%d",&n,&m);
m++;
for (int i=1;i<=n;i++)
{
scanf("%d",&sum[i]);
sum[i]+=sum[i-1];
}
for (int i=0;i<=n;i++) f[i][0]=-inf;
f[0][0]=0;
g=0;
for (int j=1;j<=m;j++)
{
h=t=1;
q[t]=0;
g^=1;
for (int i=0;i<=n;i++) f[i][g]=-inf;
for (int i=1;i<=n;i++)
{
while ((h<t) && (get(q[h],q[h+1])<=sum[i])) h++;
f[i][g]=f[q[h]][g^1]+((LL)sum[i]-(LL)sum[q[h]])*(LL)sum[q[h]];
fa[i][j]=q[h];
while((h<t) && (get(q[t-1],q[t])>=get(q[t],i))) t--;
q[++t]=i;
}
}
printf("%lld\n",f[n][g]);
int x=n,y=m;
while (x>0)
{
x=fa[x][y];
y--;
ans[++cnt]=x;
}
for (int i=1;i<=cnt-1;i++) printf("%d ",ans[i]);
}