1. 程式人生 > 其它 >玩具裝箱

玩具裝箱

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;
}
一如既往,萬事勝意