您的位置:首页 > 其它

[bzoj 3675--Apio2014]序列分割

2018-01-21 11:32 253 查看
小H最近迷上了一个分隔序列的游戏。在这个游戏里,小H需要将一个长度为n的非负整数序列分割成k+1个非空的子序列。为了得到k+1个子序列,小H需要重复k次以下的步骤:

1.小H首先选择一个长度超过1的序列(一开始小H只有一个长度为n的序列——也就是一开始得到的整个序列);

2.选择一个位置,并通过这个位置将这个序列分割成连续的两个非空的新序列。

每次进行上述步骤之后,小H将会得到一定的分数。这个分数为两个新序列中元素和的乘积。小H希望选择一种最佳的分割方式,使得k轮之后,小H的总得分最大。

(这道题告诉我,公式一定不要推错)

很明显,这题是dp,但一定不是普通·dp。方程很简单,f[i][k]=max(f[j][k-1]+(s[i]-s[j])*s[j])(分割顺序不影响,伟大的乘法分配律),再看,只发现可以用斜率优化。

设i>j>w

f[j][k-1]+(s[i]-s[j])*s[j]>f[w][k-1]+(s[i]-s[w])*s[w]

化得

(s[j]^2-f[j][k-1]+s[w]^2-f[w][k-1])/(s[j]-s[w])< s[i]

设Y[i]=s[i]^2-f[i][k-1],X[i]=s[i]

(Y[j]-Y[w])/(X[j]-X[w])< s[i]

这题解决了。(不过要用滚动数组,卡空间)

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdlib>
using namespace std;
int now=1;long long n,m,s[110000],f[2][110000],v[110000],a[110000];
long long YY(int k,int x){return s[x]*s[x]-f[1-k][x];}
long long Y(int k,int x,int y){return YY(k,y)-YY(k,x);}
long long X(int x,int y){return s[y]-s[x];}
int main()
{
int t=0;
scanf("%lld%lld",&n,&m);
m=min(m,n-1);
for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
for(int i=1;i<=n;i++){if(a[i]!=0)a[++t]=a[i];}
n=t;
for(int i=1;i<=n;i++)s[i]=s[i-1]+a[i];
memset(f,0,sizeof(f));
for(int j=1;j<=m;j++)
{
int head=1,tail=1;now=1-now;
for(int i=j;i<=n;i++)
{
while(head<tail && Y(now,v[head],v[head+1])<s[i]*X(v[head],v[head+1]))head++;
int x=v[head];
f[now][i]=f[1-now][x]+(s[i]-s[x])*s[x];
while(head<tail && Y(now,v[tail-1],v[tail])*X(v[tail],i)>Y(now,v[tail],i)*X(v[tail-1],v[tail]))tail--;
v[++tail]=i;
}
}
printf("%lld\n",f[now]
);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  dp bzoj Apio 斜率优化