玩具装箱
这道题显然可以使用斜率优化DP来解决,但为了理解新知识我还是研究了一下用决策单调性如何解决。
首先列方程。可以得出下面的式子:
\[f[x]=\min\limits_{i=0}^{x-1}\{f[i]+(x-i+1+s_x-s_i-L)^2\}
\]
对比一下模板式子:
\[f[x]=\min\limits_{i=0}^{x-1}\{f[i]+w(i,x)\}
\]
是不是一样的?接下来需要证明这个方程具有单调性,即是要证明题目中的w函数满足那个奇怪的不等式。可以把一些不变量包装起来,令
\[S=1-L
\]
那么
\[w(i,x)=(s[x]+x-(s_i+i-S))^2
\]
可以看出假如把i看成常量时这是一个\(w(i,x)\)关于\(x\)开口朝上的二次函数,而当i变大时相当于是\(s_i+i-S\)变大,函数图像整体向右平移(左加右减)。由于开口向上二次函数斜率是单调递增的,所以在相同自变量范围中右边的图像一定比左边的增加得要慢。转化成数学语言就是满足
\[\forall i\le j,w[i+1,j+1]-w[i+1,j]\le w[i,j+1]-w[i,j]
\]
移项得到
\[\forall i\le j,w[i,j]+w[i+1,j+1]\le w[i,j+1]+w[i+1,j]
\]
于是乎就满足决策单调性了(证明我也不会,四边形不等式我还没有学)。所以呢我们知道了每个点的决策点数列是一个单调不降序列,如何求它呢?有两种方法,这道题用了第一种二分法。它考虑的是对于每一个点它可能成为哪些点的决策点,很明显求出来的应该是右端点为终点的一个区间,那么我们需要做的就是求出左端点的位置。这一点就可以使用二分啦。
#include<cstdio>
//#define zczc
#define int long long
const int N=50010;
inline void read(int &wh){
wh=0;int f=1;char w=getchar();
while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
while(w<='9'&&w>='0'){wh=wh*10+w-'0';w=getchar();}
wh*=f;return;
}
int m,n,a[N],f[N],s[N],r[N],top;
struct node{int id,l;}st[N];
inline int max(int s1,int s2){return s1<s2?s2:s1;}
inline int pow(int wh){return wh*wh;}
inline int cost(int x,int y){return f[x]+pow(y-x-1+s[y]-s[x]-n);}
inline int workf(int wh){
int l=1,r=top,mid;
while(l<r)st[mid=l+r+1>>1].l<=wh?l=mid:r=mid-1;
return st[l].id;
}
signed main(){
#ifdef zczc
freopen("in.txt","r",stdin);
#endif
read(m);read(n);
for(int i=1;i<=m;i++)read(a[i]),s[i]=s[i-1]+a[i];
st[++top]=(node){0,1};
for(int i=1;i<=m;i++){
f[i]=cost(workf(i),i);r[i]=workf(i);
while(top&&st[top].l>i&&cost(st[top].id,st[top].l)>=cost(i,st[top].l))top--;
if(top==0){st[++top]=(node){i,i+1};continue;}
int l=max(st[top].l,i+1),r=m,mid;
while(l<r)cost(st[top].id,mid=l+r>>1)<cost(i,mid)?l=mid+1:r=mid;
if(cost(st[top].id,l)>=cost(i,l))st[++top]=(node){i,l};
}
printf("%lld",f[m]);
return 0;
}
一如既往,万事胜意