题目描述
你正在玩一个关于长度为 nn 的非负整数序列的游戏。这个游戏中你需要把序列分成 k + 1k+1 个非空的块。为了得到 k + 1k+1 块,你需要重复下面的操作 kk 次:
选择一个有超过一个元素的块(初始时你只有一块,即整个序列)
选择两个相邻元素把这个块从中间分开,得到两个非空的块。
每次操作后你将获得那两个新产生的块的元素和的乘积的分数。你想要最大化最后的总得分。
输入输出格式
输入格式:
第一行包含两个整数 nn 和 kk。保证 k + 1 leq nk+1≤n。
第二行包含 nn 个非负整数 a_1, a_2, cdots, a_na1,a2,⋯,an (0 leq a_i leq 10^4)(0≤ai≤104),表示前文所述的序列。
输出格式:
第一行输出你能获得的最大总得分。
第二行输出 kk 个介于 11 到 n - 1n−1 之间的整数,表示为了使得总得分最大,你每次操作中分开两个块的位置。第 ii个整数 s_isi 表示第 ii 次操作将在 s_isi 和 s_{i + 1}si+1 之间把块分开。
如果有多种方案使得总得分最大,输出任意一种方案即可。
输入输出样例
说明
你可以通过下面这些操作获得 108108 分:
初始时你有一块 (4, 1, 3, 4, 0, 2, 3)(4,1,3,4,0,2,3)。在第 11 个元素后面分开,获得 4 imes (1 + 3 + 4 + 0 + 2 + 3) = 524×(1+3+4+0+2+3)=52 分。
你现在有两块 (4), (1, 3, 4, 0, 2, 3)(4),(1,3,4,0,2,3)。在第 33 个元素后面分开,获得 (1 + 3) imes (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)。在第 55 个元素后面分开,获得 (4 + 0) imes (2 + 3) = 20(4+0)×(2+3)=20 分。
所以,经过这些操作后你可以获得四块 (4), (1, 3), (4, 0), (2, 3)(4),(1,3),(4,0),(2,3) 并获得 52 + 36 + 20 = 10852+36+20=108 分。
限制与约定
第一个子任务共 11 分,满足 1 leq k < n leq 101≤k<n≤10。
第二个子任务共 11 分,满足 1 leq k < n leq 501≤k<n≤50。
第三个子任务共 11 分,满足 1 leq k < n leq 2001≤k<n≤200。
第四个子任务共 17 分,满足 2 leq n leq 1000, 1 leq k leq min{n - 1, 200}2≤n≤1000,1≤k≤min{n−1,200}。
第五个子任务共 21 分,满足 2 leq n leq 10000, 1 leq k leq min{n - 1, 200}2≤n≤10000,1≤k≤min{n−1,200}。
第六个子任务共 29 分,满足 2 leq n leq 100000, 1 leq k leq min{n - 1, 200}2≤n≤100000,1≤k≤min{n−1,200}。
题解
- 题目大意:有一个长度为n的序列,将其分成k+1块,每次分块将两个相邻的数从中间分开,每次贡献就是新分出来的两个块的元素和的乘积,最大化贡献
- 显然,我们可以得到一个转移方程,f[i][j]=max(f[i−1][k]+sum[k]×(sum[j]−sum[k]))
- 但是直接做会炸,然后斜率优化就好了
代码
1 #include <cstdio> 2 #include <iostream> 3 using namespace std; 4 #define ll long long 5 #define sqr(x) (x)*(x) 6 #define N 100010 7 #define eps 1e18; 8 int n,k,head,tail,Q[N],ans[210][N],a[N]; 9 ll sum[N],f[2][N]; 10 double calc(int i,int j,int k) 11 { 12 if (sum[i]==sum[j]) return -eps; 13 return (f[(k+1)&1][j]-sqr(sum[j])-f[(k+1)&1][i]+sqr(sum[i]))*1.0/(sum[i]-sum[j]); 14 } 15 int main() 16 { 17 scanf("%d%d",&n,&k); 18 for (int i=1;i<=n;i++) scanf("%d",&a[i]),sum[i]=sum[i-1]+a[i]; 19 for (int i=1;i<=k;i++,head=tail=0) 20 for (int j=1;j<=n;j++) 21 { 22 while (head<tail&&calc(Q[head],Q[head+1],i)<=sum[j]) head++; 23 f[i&1][j]=f[(i+1)&1][Q[head]]+sum[Q[head]]*(sum[j]-sum[Q[head]]),ans[i][j]=Q[head]; 24 while (head<tail&&calc(Q[tail-1],Q[tail],i)>=calc(Q[tail],j,i)) tail--; 25 Q[++tail]=j; 26 } 27 printf("%lld ",f[k&1][n]); 28 for (int i=k,j=n;i>=1;i--) printf("%d ",j=ans[i][j]); 29 }