您的位置:首页 > 其它

bzoj3675 [APIO2014] 序列分割(斜率优化)

2017-08-08 16:24 337 查看
2d/1d的转移方程的斜率优化。

首先我们根据这个分割的过程可以发现:总得分等于k+1段两两的乘积的和(乘法分配律),也就是说与分割顺序是无关的。再对乘积进行重分组(还是乘法分配律)我们可以转化为:ans=∑第 i 段×前 i-1 段的和,那我们可以认为这k+1段是从左到右依次分出来的。假设目前这段是j+1…i,则他对答案的贡献为(sum[i]-sum[j])*sum[j].这样就很容易可以得到O(kn2)的状态转移方程

f[i][k]=max{f[j][k−1]+(sum[i]−sum[j])∗sum[j]|k<=j<i}

前i个数砍k刀的最大收益,枚举第k刀砍在j后面。进行斜率优化时要小心有0的存在,因此我们在读入时就把0都去掉,在n==k时及时退出,就不会影响答案。

假设k1< k2且k1优于k2,则有(f[k-1][k1]-f[k-1][k2]+sum[k2]*sum[k2]-sum[k1]*sum[k1])/(sum[k2]-sum[k1])>sum[i]。所以我们维护一个下凸曲线,保证队列中的斜率单增。用滚动数组优化空间复杂度为O(n),时间复杂度为O(nk)

#include <cstdio>
#include <cstring>
#define N 100010
#define ll long long
int a
,n,k,q
,h=0,t=1,p=0;
ll sum
,f[2]
;
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x*f;
}
inline double slope(int k1,int k2){
return (f[p][k1]-f[p][k2]+sum[k2]*sum[k2]-sum[k1]*sum[k1])*1.0/(sum[k2]-sum[k1]);
}
int main(){
//  freopen("a.in","r",stdin);
n=read();k=read();
for(int i=1;i<=n;++i){a[i]=read();if(a[i]==0) --i,--n;}
for(int i=1;i<=n;++i) sum[i]=sum[i-1]+a[i];
for(int x=1;x<=k;++x){
memset(q,0,sizeof(q));h=0,t=1;q[++h]=x;
for(int i=x+1;i<=n;++i){
while(h<t&&slope(q[h],q[h+1])<sum[i]) ++h;
f[p^1][i]=f[p][q[h]]+(sum[i]-sum[q[h]])*sum[q[h]];
while(h<t&&slope(q[t],i)<slope(q[t-1],q[t])) --t;
q[++t]=i;
}p^=1;
}
printf("%lld\n",f[p]
);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: