Link:
Solution:
首先要注意到结果与分割的顺序无关,只与最终状态有关
实际上$res=sum_{i,jle k+1} a[i]*a[j]$
可再转化为$res=sum_{i=1}^n a[i]*sum[i-1]$
令$dp[i][j]$表示将前$j$个数分成$i$段的最大得分,
$dp[i][j]=max{ dp[i−1][k]+sum[k]×(sum[j]−sum[k])}$
可以发现这个式子明显是可以斜率优化的,且能用滚动数组,直接上就行了
Tip:
1、此题卡空间,不用滚动数组会$MLE$
2、可能出现$sum[j]=sum[k]$的情况,要特判(改除法为乘法也行)
Code:
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int MAXN=1e5+10; ll INF=1e18,sum[MAXN],dp[2][MAXN]; int pre[205][MAXN],q[MAXN],n,k,x,l,r,cur=0; inline double slope(int k,int j) { if(sum[k]==sum[j]) return -INF; return (sum[k]*sum[k]-sum[j]*sum[j]+dp[cur^1][j]-dp[cur^1][k])*1.0/(sum[k]-sum[j]); } int main() { scanf("%d%d",&n,&k); for(int i=1;i<=n;i++) scanf("%d",&x),sum[i]=sum[i-1]+x; for(int cnt=1;cnt<=k;cnt++,cur^=1) { l=r=0;q[l]=0; for(int i=1;i<=n;i++) { while(l<r&&slope(q[l+1],q[l])<=sum[i]) l++; dp[cur][i]=dp[cur^1][q[l]]+sum[q[l]]*(sum[i]-sum[q[l]]); while(l<r&&slope(q[r],q[r-1])>slope(i,q[r])) r--;q[++r]=i; } } printf("%lld",dp[cur^1][n]); return 0; }
Review:
1、斜率优化中要注意除数是否可能为0!
2、感觉不少这样一边操作一边算贡献的题目最后都和中间操作无关
多考虑一下,用分配率什么的推一推看是否只和最终态有关
3、为了转化成符合$dp$的模型
将 两两间的操作 改成 一个数与之前数(之和)的操作