bzoj 1010--玩具装箱toy

我们很容易想到一个O(n^2)的转移方程:dp[i]=min(dp[i],dp[j]+(i-j-1+sum[i]-sum[j]-L)^2) (1<=j < i) 其中sum[i]是[1-i]的前缀和,但是这时间复杂度明显是不可行的,可以发现转移方程为了寻找最优的 j 将时间复杂度增加了一个维度,若是将这个过程优化到 log(n)或者O(1),就可以解决此问题了。
我们只有一个转移方程,所以我们应该对现有的转移方程进行分析。
dp[i]=min(dp[i],dp[j]+(i-j-1+sum[i]-sum[j]-L)^2) 设f[x]=sum[x]+x , L=L+1;
所以 dp[i]=min(dp[i],dp[j]+(f[i]-f[j]-L)^2)
如果 j,k< i 并且k>j
我们假设 dp[j]+(f[i]-f[j]-L)^2>dp[k]+(f[i]-f[k]-L)^2 就说明选择k优于选择j
又有t>i ,所以f[t]=f[i]+d (d>0)
如果 dp[j]+(f[t]-f[j]-L)^2>dp[k]+(f[t]-f[k]-L)^2 就说明对于 i 选择k优于选择j,相当于对于大于 i 同样有选择k优于选择j
所以我们要根据 dp[j]+(f[i]-f[j]-L)^2>dp[k]+(f[i]-f[k]-L)^2 -> dp[j]+(f[t]-f[j]-L)^2>dp[k]+(f[t]-f[k]-L)^2
dp[j]+(f[i]-f[j]-L)^2>dp[k]+(f[i]-f[k]-L)^2 -> dp[j]+(f[i]+d-f[j]-L)^2>dp[k]+(f[i]+d-f[k]-L)^2
dp[j]+(f[i]+d-f[j]-L)^2=dp[j]+(f[i]-f[j]-L)^2+d^2+2*d*(f[i]-f[j]-L)
dp[k]+(f[i]+d-f[k]-L)^2=dp[k]+(f[i]-f[k]-L)^2+d^2+2*d*(f[i]-f[k]-L)
所以只要 f[i]-f[j]-L>f[i]-f[k]-L 即f[k]>f[j] ,因为f[x]=sum[x]+x 是单调递增的,所以得证。
所以只要 dp[j]+(f[i]-f[j]-L)^2>dp[k]+(f[i]-f[k]-L)^2 成立,我们就知道>=i 选择k优于选择j
我们对方程进一步化简,可以得到 (dp[k]-dp[j]+(f[k]+L)^2-(f[j]+L)^2)/(2*(f[k]-f[j])) < f[i]
所以我们现在知道只要满足这个条件即满足选择k优于选择j 。
我们设 getK(j,k)=(dp[k]-dp[j]+(f[k]+L)^2-(f[j]+L)^2)/(2*(f[k]-f[j])) (k>j) ,可以看成k,j两点的斜率。
为了进一步优化,我们要维护这样一个数列,数列范围为[l,r],x属于[l,r],有getK(x,x+1)< getK(x+1,x+2) (x+1,x+2 属于[l,r]),可以看成斜率递增。
只要getK(k,j)< f[i]就可以知道 i 选择k优于选择j,因为数列是斜率递增的,所以我们只要找到小于f[i]的最大斜率即可,并且 i 得到的最大斜率前面的斜率都可以舍弃了。
因为f[x]也是递增的,前面的斜率小于f[i],就肯定小于f[i+1]。

要是还没理解,就看代码吧

#include<cstdio>
using namespace std;

typedef long long ll;
const int maxn=60000;

ll dp[maxn];
ll sum[maxn],f[maxn];
ll L; 
int que[maxn],l,r;

double getK(int k,int j){
	
	return 1.0*(dp[k]-dp[j]+(f[k]+L)*(f[k]+L)-(f[j]+L)*(f[j]+L))/(2.0*(f[k]-f[j]));
}

int main(){
	
	int n;
	scanf("%d%lld",&n,&L);
	L++;
    for(int i=1;i<=n;i++){
    	
    	ll tmp;
    	scanf("%lld",&tmp);
    	sum[i]=sum[i-1]+tmp;
    	f[i]=sum[i]+i;
	}
	l=1;
	r=0;
	que[++r]=0;
	for(int i=1;i<=n;i++){
		
		while(l<r&&getK(que[l+1],que[l])<f[i]) l++;
		int t=que[l];
		dp[i]=dp[t]+(f[i]-f[t]-L)*(f[i]-f[t]-L);
		while(l<r&&getK(que[r],que[r-1])>getK(i,que[r])) r--;
		que[++r]=i; 
	}
	printf("%lld\n",dp[n]);
} 
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ITKaven

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值