[APIO2014]序列分割
试题分析
其实可以发现我们选择切的顺序可以颠换。
所以我们可以设$dp(i,j)$表示为前$i$个共切$j$的最大得分,然后$dp(i,j)=max(dp(i-1,z)+s[z] \times (s[i]-s[z])),s[i]=\sum_{i=1}^i a_i$。
然后发现这是个斜率优化的式子,但是斜率只有一项,且为-$dp(i,j)$,所以我们虽然是要去维护上凸壳但其实事实上维护下凸壳。然后就基本斜率优化操作了。
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #define int long long using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int N=100011; const int K=211; int f[K][N],n,k,s[N],last[K][N],que[N],dp[N],g[N],X[N],Y[N]; signed main(){ // freopen("10.in","r",stdin); n=read(),k=read(); for(int i=1;i<=n;i++) s[i]=s[i-1]+read(); for(int p=1;p<=k;p++){ int l,r;l=r=1;que[1]=0; for(int i=1;i<=n;i++) g[i]=f[p-1][i]; Y[0]=s[0]*s[0]-g[0],X[0]=s[0]; for(int i=1;i<=n;i++){ while(l<r&&(Y[que[l+1]]-Y[que[l]])<=s[i]*(X[que[l+1]]-X[que[l]])) l++; f[p][i]=f[p-1][que[l]]+s[que[l]]*(s[i]-s[que[l]]); last[p][i]=que[l]; X[i]=s[i],Y[i]=s[i]*s[i]-g[i]; while(l<r&&(Y[que[r]]-Y[que[r-1]])*(X[i]-X[que[r]])>=(Y[i]-Y[que[r]])*(X[que[r]]-X[que[r-1]])) r--; que[++r]=i; } } int r=n; printf("%lld\n",f[k][n]); for(int i=k;i>=1;i--){ printf("%d ",last[i][r]); r=last[i][r]; } }