[APIO2014]序列分割

link

试题分析

其实可以发现我们选择切的顺序可以颠换。

所以我们可以设$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];
    }
}
View Code

 

posted @ 2018-12-14 20:59  siruiyang_sry  阅读(149)  评论(0编辑  收藏  举报