bzoj1010玩具装箱 斜率优化dp

显然f[i]=min(f[j]+(sum[i]-sum[j]+i-(j+1)-l)^2)

sum[i]表示前缀和

但显然这样的时间复杂度无法得满分,于是我们考虑斜率优化。

为了方便书写,令sum[i]=sum[i]+i,l=l+1

方程变为f[i]=min(f[j]+(sum[i]-sum[j]-l)^2)

当k状态对于当前i比j状态更优时(k>j),f[k]+(sum[i]-sum[k]-l)^2<f[j]+(sum[i]-sum[j]-l)^2

通过数学推理,可以得到f[j]+sum[j]*sum[j]-f[k]-sum[k]*sum[k]/(2*(sum[j]-sum[k])<sum[i]-l

我们令不等式左边为K(j,k)

若K(i,j)>K(j,k)则j状态在之后一定不是最优状态。

证明:假设之后一状态w

1.K(j,k)<sum[w]-l,此时k比j优,故j状态不是最优

2.K(j,k)>=sum[w]-l,由条件可推出K(i,j)>sum[w]-l,此时i比j优。证毕!

然后就可以用一个单调队列维护k(i,j)

ac代码

#include<bits/stdc++.h>
#define maxn 100005
#define ll long long 
using namespace std;
ll sum[maxn],n,m,f[maxn];
ll q[maxn];
double K(ll j,ll k)
{
	return (double)(f[j]+sum[j]*sum[j]-f[k]-sum[k]*sum[k])/(2.0*(sum[j]-sum[k]));
}
ll read()
{
	ll x=0,c;while(!isdigit(c=getchar()));
	while(x=x*10+c-'0',isdigit(c=getchar()));
	return x;
}
int main()
{
	n=read();m=read()+1;
	for(int i=1;i<=n;i++)sum[i]=sum[i-1]+read();
	for(int i=1;i<=n;i++)sum[i]+=i;
	int l=0,r=0;
	for(int i=1;i<=n;i++)
	{
		while(l<r&&K(q[l],q[l+1])<=sum[i]-m)l++;
		f[i]=f[q[l]]+(sum[i]-sum[q[l]]-m)*(sum[i]-sum[q[l]]-m);
		while(l<r&&K(q[r-1],q[r])>=K(q[r],i))r--;
		q[++r]=i;
	}
	printf("%lld
",f[n]);
	return 0;
}

  

原文地址:https://www.cnblogs.com/hlsb/p/8541687.html