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

【题解】

一个重要的结论:

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

不妨考虑最先分成的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][n]);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值