【APIO2014】序列分割

Description

【APIO2014】序列分割_第1张图片

Solution

怎么做

发现长得好像什么石子合并之内的题目,然后探究探究,得出了一个结论:分割点相同,分割的顺序是没有关系的。
然后就想到了DP。设f[i][j]表示在第i次分割时,在j点分割。
转移引进一个x就可以了f[i][j]=max(f[x][j-1]+sum[x]*(sum[i]-sum[x])),打个前缀和,很显然。
复杂度 (O(n2k))

斜率优化

发现DP的有一维其实是没有什么用的,可以打成滚动数组,然后就只剩下一个有用维度了,一般只用一维的DP都可以打斜率优化。
打完斜率优化得到个这个东西:

f[x]s[x]2f[y]+s[y]2s[y]s[x]>s[i]

然后用单调队列存储就可以了。

Code

    #include
    #include
    #include
#include
#include
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
typedef long long ll;
using namespace std;
const int maxn=100007;
int i,j,k,t,n,m,x,v;
int g[maxn][207];
int d[maxn],l,r,ans[maxn];
ll a[maxn],sum[maxn],f[2][maxn];
ll mu(int x,int y,int i){
    return f[i][x]-sum[x]*sum[x]-f[i][y]+sum[y]*sum[y];
}
ll zi(int x,int y){
    return sum[y]-sum[x];
}
int main(){
    freopen("sequence.in","r",stdin);
    freopen("sequence.out","w",stdout);
    scanf("%d%d",&n,&m);
    fo(i,1,n)scanf("%lld",&a[i]),sum[i]+=sum[i-1]+a[i];
    fo(j,1,m){
        l=1,r=0,d[l]=0,v^=1;
        fo(i,1,n){
            while(l1],v^1)<=zi(d[l],d[l+1])*sum[i])l++;
            f[v][i]=f[v^1][d[l]]+sum[d[l]]*(sum[i]-sum[d[l]]);
            g[i][j]=d[l];
            while(r>l&&mu(d[r-1],d[r],v^1)*zi(d[r],i)>=zi(d[r-1],d[r])*mu(d[r],i,v^1))r--;
            d[++r]=i;
        }
    }
    printf("%lld\n",f[v][n]);
    j=0;
    for(i=g[n][m];i;i=g[i][m-j]){
        j++;ans[j]=i;
    }
    fo(i,1,m)printf("%d ",ans[i]);
}

你可能感兴趣的:(DP,省选,斜率优化,单调队列)