题目描述
你正在玩一个关于长度为 n 的非负整数序列的游戏。这个游戏中你需要把序列分成 k+1 个非空的块。为了得到 k+1 块,你需要重复下面的操作 k 次:
选择一个有超过一个元素的块(初始时你只有一块,即整个序列)
选择两个相邻元素把这个块从中间分开,得到两个非空的块。
每次操作后你将获得那两个新产生的块的元素和的乘积的分数。你想要最大化最后的总得分。
输入格式
第一行包含两个整数 n 和 k。保证 k+1≤n。
第二行包含 n 个非负整数 a1,a2,⋯ ,ana_1, a_2, \cdots, a_na1,a2,⋯,an(0≤ai≤104)(0≤ai≤10^4)(0≤ai≤104),表示前文所述的序列。
输出格式
第一行输出你能获得的最大总得分。
第二行输出 k 个介于 1 到 n - 1 之间的整数,表示为了使得总得分最大,你每次操作中分开两个块的位置。第 i 个整数 sis_isi 表示第 i 次操作将在 sis_isi 和 si+1s_{i + 1}si+1 之间把块分开。
如果有多种方案使得总得分最大,输出任意一种方案即可。
输入输出样例
输入 #1
7 3
4 1 3 4 0 2 3
输出 #1
108
1 3 5
说明/提示
2≤n≤100000,1≤k≤min{n−1,200}2≤n≤100000,1≤k≤min\{n−1,200\}2≤n≤100000,1≤k≤min{n−1,200}
解释:dp[k][i]:dp[k][i]:dp[k][i]:在i出化第k刀的最大值,则dp[k][i]=max{dp[k−1][j]+(sum(i)−sum(j))∗(sum(n)−sum(i))}dp[k][i]=max\{dp[k-1][j]+(sum(i)-sum(j))*(sum(n)-sum(i))\}dp[k][i]=max{dp[k−1][j]+(sum(i)−sum(j))∗(sum(n)−sum(i))}
我们枚举k,进行斜率优化就可以了,老套路
设j>kj>kj>k,且jjj优于kkk,则最后可以化得(dp[j]−dp[k])/(sum[j]−sum[k])>sum[n]−sum[i](dp[j]-dp[k])/(sum[j]-sum[k])>sum[n]-sum[i](dp[j]−dp[k])/(sum[j]−sum[k])>sum[n]−sum[i],然后套斜率优化模板就好了。注意0要单独处理掉,0不会对答案最贡献,但可以出现斜率不存在的情况。
#include<cstring>
#include<iostream>
#define ll long long
#define N 100004
using namespace std;
int n=0,k=0;
ll sum[N]={0};
int q[N]={0};
ll dp[203][N]={0};
int front[203][N]={0};
int ans[203]={0};
int temp[N]={0};
int t=0;
double cal(int x,int y,int k){
return 1.0*(dp[k][x]-dp[k][y])/(sum[x]-sum[y]);
}
int main(){
ios::sync_with_stdio(false);
cin>>n>>k;
for(int i=1,a;i<=n;i++){
cin>>a;
if(a){
++t;
sum[t]=sum[t-1]+a;
temp[t]=i;
}
}
n=t;t=0;
for(int i=1;i<=k;i++){
memset(dp[i],0,sizeof(dp[i]));
q[1]=0;
for(int j=1,l=1,r=1;j<=n;j++){
while(l<r&&cal(q[l],q[l+1],i-1)>=1.0*(sum[n]-sum[j])) l++;
dp[i][j]=dp[i-1][q[l]]+(sum[j]-sum[q[l]])*(sum[n]-sum[j]);
front[i][j]=q[l];
while(l<r&&cal(q[r],q[r-1],i-1)<=cal(q[r],j,i-1)) --r;
q[++r]=j;
}
}
int ret=0;
for(int i=1;i<=n;i++) if(dp[k][ret]<=dp[k][i]) ret=i;
ans[1]=ret;
for(int i=2;i<=k;i++) ans[i]=front[k-i+2][ans[i-1]];
cout<<dp[k][ret]<<endl;
for(int i=k;i>=1;i--) cout<<temp[ans[i]]<<" ";
cout<<endl;
return 0;
}