玩具装箱

link

这道题显然可以使用斜率优化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;
}
上一篇:算法-栈和队列:接雨水


下一篇:java操作JDBC实现增删改查工具类