[BZOJ 3675] 序列分割

Link:

BZOJ 3675 传送门

Solution:

首先要注意到结果与分割的顺序无关,只与最终状态有关

实际上$res=\sum_{i,j\le 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$的模型

两两间的操作 改成 一个数与之前数(之和)的操作

 

posted @ 2018-07-13 13:26  NewErA  阅读(181)  评论(0编辑  收藏  举报