[HNOI2008]玩具装箱TOY(斜率优化)
[HNOI2018]玩具装箱TOY
这道题需要用到斜率优化,不会的同学点此
题目描述
P教授要去看奥运,但是他舍不下他的玩具,于是他决定把所有的玩具运到北京。他使用自己的压缩器进行压缩,其可以将任意物品变成一堆,再放到一种特殊的一维容器中。P教授有编号为1...N的N件玩具,第i件玩具经过压缩后变成一维长度为Ci.为了方便整理,P教授要求在一个一维容器中的玩具编号是连续的。同时如果一个一维容器中有多个玩具,那么两件玩具之间要加入一个单位长度的填充物,形式地说如果将第i件玩具到第j个玩具放到一个容器中,那么容器的长度将为x=j-i+\(\sum_{K=i}^{j}{CK}\)制作容器的费用与容器的长度有关,根据教授研究,如果容器长度为x,其制作费用为(X-L)^2.其中L是一个常量。P教授不关心容器的数目,他可以制作出任意长度的容器,甚至超过L。但他希望费用最小.
输入输出格式
输入格式:
第一行输入两个整数N,L.接下来N行输入Ci.1<=N<=50000,1<=L,Ci<=10^7
输出格式:
输出最小费用
输入输出样例
输入样例:
5 4
3
4
2
1
4
输出样例:
1
这道题目是一道很好的斜率优化入门题,状态转移方程应该不难得出
\[dp[i]=min(dp[j]+(sum[i]+i-sum[j]-j-L-1)^2) (j<i)
\]
\(sum[i]\) 为前缀和,表示放入前i件物品所占的长度。
令
\[a[i]=sum[i]+i-L-1
\]
\[b[j]=sum[j]+j
\]
那么此时dp方程可转化为
\[dp[i]=dp[j]+(a[i]-b[j])^2 (j<i)
\]
进一步化简
\[dp[i]=dp[j]+a[i]^2-2*a[i]*b[j]+b[j]^2
\]
移项,得
\[dp[j]+b[j]^2=2a[i]*b[j]+dp[i]-a[i]^2
\]
如果你学会了斜率优化,到这一步就很明了了
\(b[j]\)看作\(x\),\(dp[j]+b[j]^2\)看作\(y\),斜率为\(2a[i]\)的直线
\(\because\) \(a[i]\) 单调递增
\(\therefore\) 我们需要维护一个下凸包
又\(\because\) \(b[j]\)也单调递增
\(\therefore\) 用队列维护即可
代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#define ll long long
using namespace std;
ll read()
{
ll x=0,w=1;char ch=getchar();
while(ch>'9'||ch<'0') {if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x*w;
}
ll dp[50010],sum[50010];
ll team[50010];
long long get(int x)
{
return dp[x]+(x+sum[x])*(x+sum[x]);
}
int main()
{
int n=read(),lll=read(),a;
for(int i=1;i<=n;i++)
{
a=read();
sum[i]=sum[i-1]+a;
}
dp[0]=0;
int l=1,r=1;
for(int i=1;i<=n;i++)
{
ll qwe=i+sum[i]-lll-1;
while(l<r&&get(team[l+1])-get(team[l])<=2*qwe*(team[l+1]-team[l]+sum[team[l+1]]-sum[team[l]])) l++;
dp[i]=get(team[l])-2*qwe*(team[l]+sum[team[l]])+qwe*qwe;
while(l<r&& (get(team[r])-get(team[r-1])) * (i+sum[i]-team[r]-sum[team[r]]) >= (get(i)-get(team[r])) * (team[r]+sum[team[r]]-team[r-1]-sum[team[r-1]]) )
r--;
team[++r]=i;
/*for(int j=0;j<i;j++)
{
if(dp[i]>dp[j]+(i-j-1+sum[i]-sum[j]-l)*(i-j-1+sum[i]-sum[j]-l))
{
dp[i]=dp[j]+(i-j-1+sum[i]-sum[j]-l)*(i-j-1+sum[i]-sum[j]-l);
}
}*/
}
cout<<dp[n];
}