您的位置:首页 > 其它

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

2015-06-26 03:22 316 查看
【题解】

一个重要的结论:

对于同一组分割方式,总得分与分割的先后顺序无关

不妨考虑最先分成的3部分,设区间和分别为Sa,Sb,Sc

可以证明,先分割a,b还是b,c,最终得分都是ab+bc+ca,即最先分成的3部分无需考虑顺序,子问题也是一样

于是,从前往后切割即可

设f[x][i]为前i个数分x份的最大得分,显然1<=x<=k+1,x<=i,S[i]为前缀和

dp方程:f[x][i]=max{ f[x-1][j] + S[j]*(S[i]-S[j]) },

设对于i,j比k优,代入上式并拆开

设y[j]=S[j]^2-f[x-1][j]

可以转化为维护斜率为:(y[j]-y[k])/(S[j]-S[k])的下凸壳

显然第一维可以状压成两行,不难处理

注意:a[i]可能为0,那么斜率的分母也可能为0

用这个方法避免复杂判断:若分母为0,根据y[j]-y[k]的正负返回 INF,0或-INF

这个略坑啊。

【代码】

#include<stdio.h>
#include<stdlib.h>
#define INF 100000000000000000.0
typedef long long LL;
LL s[100005],f[2][100005],y[100005];
int q[100005];
double K(int a,int b)
{
	LL dy=y[a]-y[b],dx=s[a]-s[b];
	if(dx==0)//注意分母可能为0
	{
		if(dy>0) return INF;
		if(dy==0) return 0;
		return -INF;
	}
	return (double)dy/(double)dx;
}
int main()
{
	int n,k,x,i,head,tail;
	scanf("%d%d",&n,&k);
	k++;
	for(i=1;i<=n;i++)
	{
		scanf("%lld",&s[i]);
		s[i]+=s[i-1];
	}
	for(x=2;x<=k;x++)
	{	
		head=0;
		tail=1;
		q[0]=x-1;
		y[x-1]=s[x-1]*s[x-1]-f[x-1&1][x-1];
		for(i=x;i<=n;i++)
		{
			while( tail-head>1 && K(q[head],q[head+1]) < s[i] ) head++;
			f[x&1][i]=f[x-1&1][q[head]]+s[q[head]]*(s[i]-s[q[head]]);
			y[i]=s[i]*s[i]-f[x-1&1][i];
			while( tail-head>1 && K(q[tail-2],q[tail-1]) > K(q[tail-1],i) ) tail--;
			q[tail++]=i;
		}
	}
	printf("%lld",f[k&1]
);
	return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: