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;
}