洛谷P3195玩具装箱(斜率优化dp)WA0pts求调

[HNOI2008] 玩具装箱

题目描述

P 教授要去看奥运,但是他舍不下他的玩具,于是他决定把所有的玩具运到北京。他使用自己的压缩器进行压缩,其可以将任意物品变成一堆,再放到一种特殊的一维容器中。

P 教授有编号为 1 ~n 的 n 件玩具,第 i件玩具经过压缩后的一维长度为 C_i。

为了方便整理,P 教授要求:

  • 在一个一维容器中的玩具编号是连续的。

  • 同时如果一个一维容器中有多个玩具,那么两件玩具之间要加入一个单位长度的填充物。形式地说,如果将第 i 件玩具到第 j 个玩具放到一个容器中,那么容器的长度将为 x=j-i+ci+c[i+1]+…+c[j]。

制作容器的费用与容器的长度有关,根据教授研究,如果容器长度为 x,其制作费用为 (x-L)^2。其中 L 是一个常量。P 教授不关心容器的数目,他可以制作出任意长度的容器,甚至超过 L。但他希望所有容器的总费用最小。

输入格式

第一行有两个整数,用一个空格隔开,分别代表 n 和 L。

第 2 到 第 (n + 1) 行,每行一个整数,第 (i + 1) 行的整数代表第 i件玩具的长度 C_i。

输出格式

输出一行一个整数,代表所有容器的总费用最小是多少。

样例 #1

样例输入 #1

5 4
3
4
2
1
4

样例输出 #1

1

提示

对于全部的测试点,1<=n<=5*10^4,1<=L<=10^7,1<=Ci<=10^7

定义dp[i]=j表示假设只有前i个,得到的最小值为j
状态转移方程dp[i]=min(dp[j]+(j-i-1+pre[i]-pre[j])-L)^2) (0<=j<i) //dp[i]=k表示前i位的答案为j,pre为前缀和数组

#include<bits/stdc++.h>
using namespace std;
int n,l,a[50010],cnt;
long long pre[50010],dp[50010],k[50010];
struct Node
{
	long long x,y;
};
Node x[50010];
int main()
{
	cin>>n>>l;
	for(int i=1;i<=n;i++)
	cin>>a[i];
	for(int i=1;i<=n;i++)
	pre[i]=pre[i-1]+a[i];
	dp[1]=(a[1]-l)*(a[1]-l);
	x[++cnt]={2*(pre[1]+1),dp[1]+(pre[1]+1)*pre[1]+1};
	for(int i=2;i<=n;i++)
	{
		int le=1,r=cnt-1,mid,u,v;
		while(l<r)
		{
			mid=le+r>>1;
			if(k[mid]<=pre[i]+i-1-l)
			le=mid;
			else
			r=mid-1;
		}
		if(le==1)
		{
			if(k[le]<=pre[i]+i-1-l&&pre[i]+i-1-l<=k[le+1])
			{
				dp[i]=x[le+1].y-x[le+1].x*(pre[i]+i-1-l);
			}
			else
			dp[i]=x[le].y-x[le].x*(pre[i]+i-1-l);
		}
		else
		{
			if(k[le-1]<=pre[i]+i-1-l&&pre[i]+i-1-l<=k[le])
			{
				dp[i]=x[le].y-x[le].x*(pre[i]+i-1-l);
			}
			else
			dp[i]=x[le+1].y-x[le+1].x*(pre[i]+i-1-l);
		}
		dp[i]+=(pre[i]+i-1-l)*(pre[i]+i-1-l);
		dp[i]=min(dp[1],(pre[i]+i-1-l)*(pre[i]+i-1-l));
		u=2*(pre[i]+i),v=dp[i]+(pre[i]+i)*(pre[i]+i);
		while((v-x[cnt].y)/(u-x[cnt].x)<k[cnt-1]&&cnt!=1)
		cnt--;
		k[cnt]=(v-x[cnt].y)/(u-x[cnt].x);
		x[++cnt]={v,u};
	}
	cout<<dp[n];
}

样例竟然过了

1 个赞