题目描述
你正在玩一个关于长度为 n 的非负整数序列的游戏。这个游戏中你需要把序列分成 k+1 个非空的块。为了得到 k+1 块,你需要重复下面的操作 k 次:
选择一个有超过一个元素的块(初始时你只有一块,即整个序列)
选择两个相邻元素把这个块从中间分开,得到两个非空的块。
每次操作后你将获得那两个新产生的块的元素和的乘积的分数。你想要最大化最后的总得分。
输入格式
第一行包含两个整数 n 和 k。保证 k+1≤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≤ai≤10^4) (0≤ai≤104),表示前文所述的序列。
输出格式
第一行输出你能获得的最大总得分。
第二行输出 k 个介于 1 到 n - 1 之间的整数,表示为了使得总得分最大,你每次操作中分开两个块的位置。第 i 个整数 s i s_i si 表示第 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
说明/提示
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}
解释: d p [ k ] [ i ] : dp[k][i]: dp[k][i]:在i出化第k刀的最大值,则 d p [ k ] [ i ] = m a x { d p [ k − 1 ] [ j ] + ( s u m ( i ) − s u m ( j ) ) ∗ ( s u m ( n ) − s u m ( 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 > k j>k j>k,且 j j j优于 k k k,则最后可以化得 ( d p [ j ] − d p [ k ] ) / ( s u m [ j ] − s u m [ k ] ) > s u m [ n ] − s u m [ 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;
}