BZOJ 3156: 防御准备
这一题看一眼就是 $dp$,发现限制是在右边,不妨把数列反过来,这样限制在左边比较舒服
然后显然地设 $f[i]$ 表示位置 $i$ 强制放守卫时控制 $[1,i]$ 的最小费用
那么转移直接枚举上一个守卫 $j$,因为之间放置木偶的花费为 $1+2+...+(i-j-1)=(i-j-1)(i-j)/2$,有转移:
$f[i]=f[j]+a[i]+(i-j-1)(i-j)/2$
发现后面这个是个二次函数,考虑斜率优化
把式子拆开,$f[i]=f[j]+a[i]+(i^2-i)/2+(j^2+j)/2-ij$,移项得 $f[j]+(j^2+j)/2=ij+f[i]-a[i]-(i^2-i)/2$
那么 $y=f[j]+(j^2+j),k=i,x=j,b=f[i]-a[i]-(i^2-i)/2$,因为 $k,x$ 都单调,直接斜率优化即可
最后答案是 $f[n+1]$ ,因为 $f[i]$ 表示 $i$ 强制放守卫
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> using namespace std; typedef long long ll; inline int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } const int N=1e6+7; int n,a[N]; ll f[N]; int Q[N],L,R; inline int X(int j) { return j; } inline int Y(int j) { return f[j]+(1ll*j*j+j)/2; } inline ll calc(int i,int j) { return f[j]+a[i]+1ll*(i-j-1)*(i-j)/2; } inline long double slope(int i,int j) { return (long double)(Y(i)-Y(j))/(X(i)-X(j)); } int main() { n=read(); for(int i=1;i<=n;i++) a[i]=read(); reverse(a+1,a+n+1); L=R=1; Q[L]=1; f[1]=a[1]; for(int i=2;i<=n+1;i++) { while(L<R && calc(i,Q[L])>=calc(i,Q[L+1])) L++; f[i]=calc(i,Q[L]); while(L<R && slope(Q[R-1],i)<=slope(Q[R-1],Q[R])) R--; Q[++R]=i; } printf("%lld\n",f[n+1]); return 0; }