[HNOI2008]玩具装箱TOY
题面在这里
题意
P教授有\(n\)个玩具,第\(i\)个玩具的长度为\(c_i\),
把这\(n\)个玩具分成若干段,
每一段的长度为\(x=(r-l+\sum_{k=l}^{r}{c_k})^2\),代价为\((x-L)^2\),
求把\(n\)个玩具分成若干段的最小代价
数据范围
\[1\le N\le50000,1\le L,c_i\le10^7
\]
sol
记\(sum[i]=\sum_{k=1}^{i}c[k]\)
我们很快就可以想到一个朴素的DP:设\(f[i]\)为只考虑前\(i\)个玩具的最小代价,有
\[f[i]=min_{j=0}^{i-1}{[f[j]+(i-j-1+s[i]-s[j]-L)^2]}
\]
\(1D/1D\),复杂度为\(O(n^2)\)
斜率优化(单调队列)
记\(sum[k]=s[k]+k\),\(l=L+1\),有
\[f[i]=min_{j=0}^{i-1}{[f[j]+(sum[i]-sum[j]-l)^2]}
\]
\[=min_{j=0}^{i-1}{[f[j]+(sum[i]-l)^2+sum[j]^2-2 sum[j](sum[i]-l)]}
\]
\[=min_{j=0}^{i-1}{[f[j]+sum[j]^2-2 sum[j](sum[i]-l)]}+(sum[i]-l)^2
\]
外面的部分忽略,
令\(f[j]+sum[j]^2=y_j\),\(sum[j]=x_j\),\(2(sum[i]-l)=k_i\),
于是我们有\(f[i]-(sum[i]-l)^2=min(y_j-k_ix_j)\)
由于直线方程\(y=kx+b\)于是\(b=y-kx\),
在\(k\)单调递增的情况下,我们最小化的目标是截距
于是通过单调队列维护斜率递增的点集\((x_i,y_i)\)。
询问:取出队头节点直到\(k\le \frac{y_{l+1}-y_l}{x_{l+1}-x_l}\),
也就是\(k\times(x_{l+1}-x_l)\le y_{l+1}-y_l\),
然后以队头结点作为答案
插点:弹出队尾直到斜率递增(\(\frac{y_{r-1}-y_r}{x_{r-1}-x_r}\le\frac{y_r-qy}{x_r-qx}\)),
也就是\((y_{r-1}-y_r)\times(x_r-qx)\le (y_r-qy)\times(x_{r-1}-x_r)\)
代码
#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<iomanip>
#include<cstring>
#include<complex>
#include<vector>
#include<cstdio>
#include<string>
#include<bitset>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#define mp make_pair
#define pb push_back
#define RG register
#define il inline
using namespace std;
typedef unsigned long long ull;
typedef vector<int>VI;
typedef long long ll;
typedef double dd;
const dd eps=1e-10;
const int mod=1e8;
const int N=50010;
il ll read(){
RG ll data=0,w=1;RG char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
if(ch=='-')w=-1,ch=getchar();
while(ch<='9'&&ch>='0')data=data*10+ch-48,ch=getchar();
return data*w;
}
il void file(){
freopen(".in","r",stdin);
freopen(".out","w",stdout);
}
ll n,l,c[N],s[N],sum[N],f[N];
struct node{ll x,y;};node Q[N];ll L=1,R;
il void insert(node q){
while(L<R&&(Q[R-1].y-Q[R].y)*(Q[R].x-q.x)>(Q[R].y-q.y)*(Q[R-1].x-Q[R].x))R--;
Q[++R]=q;
}
il ll query(ll k){
while(L<R&&k*(Q[L+1].x-Q[L].x)>Q[L+1].y-Q[L].y)L++;
return Q[L].y-k*Q[L].x;
}
int main()
{
n=read();l=read()+1;
for(RG int i=1;i<=n;i++)
c[i]=read(),s[i]=s[i-1]+c[i],sum[i]=s[i]+i;
insert((node){0,0});
for(RG int i=1;i<=n;i++){
f[i]=query(2*(sum[i]-l))+(sum[i]-l)*(sum[i]-l);
insert((node){sum[i],f[i]+sum[i]*sum[i]});
}
printf("%lld\n",f[n]);
return 0;
}