[bzoj3675]序列分割
斜率优化
不难发(cai)现(ce)答案与分割的顺序无关,所以可以直接从左到右分割来算
转移就是:
f[i][k]=max{f[j][k-1]+l[i]*r[i+1]-l[j]*r[i+1]}
r[i+1]是单减的,于是对于点 (l[j],f[j][[k-1]) 维护上凸壳,跑斜率优化即可。
- 代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5;
ll a[N],l[N],r[N];
ll f[N];
ll lst[N];
int st[N],top,u;
int n,k;
double X(int j){
return (double)(l[j]);
}
double Y(int j){
return (double)(lst[j]);
}
double calc(int i,int j){
double deltx=X(j)-X(i);
double delty=Y(j)-Y(i);
if(deltx==0){
if(delty>0)return 1e20;
else return -1e20;
}
return delty/deltx;
}
int main()
{
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
l[i]=l[i-1]+a[i];
}
for(int i=n;i;i--){
r[i]=r[i+1]+a[i];
}
for(int i=1;i<n;i++)f[i]=l[i]*r[i+1];
for(int t=2;t<=k;t++){
for(int i=1;i<n;i++){lst[i]=f[i];f[i]=0;}
top=0;
u=1;
for(int i=1;i<=t-1;i++)if(lst[i]){
while(top>1&&calc(st[top-1],st[top])<calc(st[top-1],i))top--;
st[++top]=i;
}
for(int i=t;i<n;i++){
while(u<top&&calc(st[u],st[u+1])>=(double)r[i+1])u++;
int j=st[u];
f[i]=lst[j]+l[i]*r[i+1]-l[j]*r[i+1];
if(lst[i]){
while(top>1&&calc(st[top-1],st[top])<calc(st[top-1],i))top--;
st[++top]=i;
}
}
}
ll ans=0;
for(int i=k;i<n;i++)ans=max(ans,f[i]);
printf("%lld\n",ans);
}