P3195 [HNOI2008]玩具装箱TOY

题目

纪念不看题解\(A\)掉的第一个斜率优化dp:P3195 [HNOI2008]玩具装箱TOY

一个月后补的重点:斜率优化的本质就是利用推出来的公式决定维护一个凸包,用单调队列来维护这个凸包,因为我们假定\(j<k,dp_j>dp_k\),所以每次决策显然就取队头,从而得到最优解

做法

定义数组\(sum_i\)为长度前缀和,数组\(dp_i\)为前\(i\)个玩具的最小花费

当前块末\(i\)与前一块末\(j\)状态转移为\(dp_i=dp_{j-1}+i-(j+1)+sum_i-sum_j-L\)

假设\(j<k\),从\(k\)转移比\(j\)转移更优:

\(dp_j+[i-(j+1)+sum_i-sum_j-L]^2>\)

\(dp_k+[i-(k+1)+sum_i-sum_k-L]^2\)

\(\Longrightarrow\)

\(dp_j+[(i+sum_i-L)+(-j-1-sum_j)]^2>\)

\(dp_k+[(i+sum_i-L)+(-k-1-sum_k)]^2\)

\(\Longrightarrow\)

\(dp_j+(i+sum_i-L)^2+2(i+sum_i-L)(-j-1-sum_j)+(-j-1-sum_j)^2>\)

\(dp_k+(i+sum_i-L)^2+2(i+sum_i-L)(-k-1-sum_k)+(-k-1-sum_k)^2\)

\(\Longrightarrow\)

\(dp_j+2(i+sum_i-L)(-j-1-sum_j)+(-j-1-sum_j)^2>\)

\(dp_k+2(i+sum_i-L)(-k-1-sum_k)+(-k-1-sum_k)^2\)

\(\Longrightarrow\)

\(dp_j-dp_k+(-j-1-sum_j)^2-(-k-1-sum_k)^2>\)

\((i+sum_i-L) \{2[(-k-1-sum_k)-(-j-1-sum_j)]\}\)

\(\Longrightarrow\)

\(\dfrac{dp_j-dp_k+(-j-1-sum_j)^2-(-k-1-sum_k)^2}{2[(-k-1-sum_k)-(-j-1-sum_j)]}>i+sum_i-L\)

:然后你就会样例都过不了,为什么?根据单调性\(2[(-k-1-sum_k)-(-j-1-sum_j)]\)为负,符号要变,所以最后的式子为

\(\dfrac{dp_j-dp_k+(-j-1-sum_j)^2-(-k-1-sum_k)^2}{2[(-k-1-sum_k)-(-j-1-sum_j)]}<i+sum_i-L\)

\(i+sum_i-L\)单调递增,所以我们用单调队列维护上凸包

My complete code

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL maxn=200000;
inline LL Read(){
	LL x=0,f=1; char c=getchar();
	while(c<'0'||c>'9'){
		if(c=='-') f=-1; c=getchar();
	}
	while(c>='0'&&c<='9')
	    x=(x<<3)+(x<<1)+c-'0',c=getchar();
	return x*f;
}
LL n,L,head,tail;
LL sum[maxn],dp[maxn],q[maxn];
inline double Get(LL i){
	return (double)i+sum[i]-L;
}
inline LL P_(LL x){
	return x*x;
}
inline double Xl(LL j,LL k){
	return (double)
	(dp[j]-dp[k]+P_(-j-1-sum[j])-P_(-k-1-sum[k]))/(2*(-k-1-sum[k]+j+1+sum[j]));
}
int main(){
	n=Read(),L=Read();
	for(LL i=1;i<=n;++i)
		sum[i]=sum[i-1]+Read();
	dp[1]=P_(sum[1]-L),
	q[head=tail=1]=1;
	for(LL i=2;i<=n;++i){
		while(head<tail&&Xl(q[head],q[head+1])<Get(i))
		    ++head;
		LL j=q[head];
		LL val=i-(j+1)+sum[i]-sum[j]-L;
		dp[i]=min(dp[j]+P_(val),P_(i-1+sum[i]-L));
		while(head<tail&&Xl(q[tail-1],q[tail])>Xl(q[tail],i))
		    --tail;
		q[++tail]=i;
	}
	printf("%lld",dp[n]);
    return 0;
}
posted @ 2018-12-25 20:02  y2823774827y  阅读(284)  评论(0编辑  收藏  举报