2018.09.29 bzoj3675: [Apio2014]序列分割(斜率优化dp)

183 篇文章 0 订阅
13 篇文章 0 订阅

传送门
斜率优化dp经典题目。
首先需要证明只要选择的K个断点是相同的,那么得到的答案也是相同的。
根据分治的思想,我们只需要证明有两个断点时成立,就能推出K个断点时成立。
我们设两个断点分成的三段连续序列的和为 a , b , c a,b,c a,b,c
如果先分左边有: t o t a l = a ∗ ( b + c ) + b ∗ c = a ∗ b + b ∗ c + c ∗ a total=a*(b+c)+b*c=a*b+b*c+c*a total=a(b+c)+bc=ab+bc+ca
如果先分右边有: t o t a l = ( a + b ) ∗ c + a ∗ b = a ∗ b + b ∗ c + c ∗ a total=(a+b)*c+a*b=a*b+b*c+c*a total=(a+b)c+ab=ab+bc+ca
是相同的。
那么这样我们就可以按从左到右划分断点的思路来进行dp了。
f [ i ] [ j ] f[i][j] f[i][j]为前j个数i个断点能够划分出的最大值。
那么显然有:
f [ i ] = m a x f[i]=max f[i]=max{ f [ j ] + s u m [ j ] ∗ ( s u m [ i ] − s u m [ j ] ) f[j]+sum[j]*(sum[i]-sum[j]) f[j]+sum[j](sum[i]sum[j])}
f [ i ] = m a x f[i]=max f[i]=max{ f [ j ] − s u m [ j ] 2 f[j]-sum[j]^2 f[j]sum[j]2} + s u m [ i ] ∗ s u m [ j ] +sum[i]*sum[j] +sum[i]sum[j]
i f ( k 1 &lt; k 2 if(k1&lt;k2 if(k1<k2&& c a l c ( k 1 ) ≤ c a l c ( k 2 ) ) calc(k1)\le calc(k2)) calc(k1)calc(k2))队头出队
=> f [ k 1 ] − s u m [ k 1 ] 2 + s u m [ i ] ∗ s u m [ k 1 ] ≤ f [ k 2 ] − s u m [ k 2 ] 2 + s u m [ i ] ∗ s u m [ k 2 ] f[k1]-sum[k1]^2+sum[i]*sum[k1]\le f[k2]-sum[k2]^2+sum[i]*sum[k2] f[k1]sum[k1]2+sum[i]sum[k1]f[k2]sum[k2]2+sum[i]sum[k2]
=> f [ k 1 ] − s u m [ k 1 ] 2 − f [ k 2 ] + s u m [ k 2 ] 2 ≤ ( s u m [ k 2 ] − s u m [ k 1 ] ) ∗ s u m [ i ] f[k1]-sum[k1]^2-f[k2]+sum[k2]^2\le (sum[k2]-sum[k1])*sum[i] f[k1]sum[k1]2f[k2]+sum[k2]2(sum[k2]sum[k1])sum[i]
=> ( ( f [ k 1 ] − s u m [ k 1 ] 2 ) − ( f [ k 2 ] − s u m [ k 2 ] 2 ) ) / ( s u m [ k 2 ] − s u m [ k 1 ] ≤ s u m [ i ] ((f[k1]-sum[k1]^2)-(f[k2]-sum[k2]^2))/(sum[k2]-sum[k1]\le sum[i] ((f[k1]sum[k1]2)(f[k2]sum[k2]2))/(sum[k2]sum[k1]sum[i]
队尾出队: s l o p e ( q [ t l − 1 ] , q [ t l ] ) &gt; = s l o p e ( q [ t l ] , i ) slope(q[tl-1],q[tl])&gt;=slope(q[tl],i) slope(q[tl1],q[tl])>=slope(q[tl],i)
代码:

#include<bits/stdc++.h>
#define N 100005
#define ll long long
using namespace std;
inline ll read(){
	ll ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
int n,K,hd,tl,q[N],tmp;
ll f[2][N],sum[N];
inline ll calcX(int i,int j){return sum[j]-sum[i];}
inline ll calcY(int i,int j){return (f[tmp^1][i]-sum[i]*sum[i])-(f[tmp^1][j]-sum[j]*sum[j]);}
int main(){
	n=read(),K=read(),tmp=0;
	for(int i=1;i<=n;++i)sum[i]=sum[i-1]+read();
	for(int i=1;i<=K;++i){
		hd=tl=1;
		for(int j=1;j<=n;++j){
			while(hd<tl&&calcY(q[hd],q[hd+1])<=calcX(q[hd],q[hd+1])*sum[j])++hd;
			int k=q[hd];
			f[tmp][j]=f[tmp^1][k]+sum[k]*(sum[j]-sum[k]);
			while(hd<tl&&calcY(q[tl-1],q[tl])*calcX(q[tl],j)>=calcY(q[tl],j)*calcX(q[tl-1],q[tl]))--tl;
			q[++tl]=j;
		}
		tmp^=1;
	}
	printf("%lld",f[tmp^1][n]);
	return 0;
} 
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值