BZOJ3156 防御准备(动态规划+斜率优化)
设f[i]为在i放置守卫塔时1~i的最小花费。那么显然f[i]=min(f[j]+(i-j)*(i-j-1)/2)+a[i]。
显然这是个斜率优化入门题。将不与i、j同时相关的提出,得f[i]=min(f[j]+j*(j+1)/2-ij)+i*(i-1)/2+a[i]。
套路地,假设j>k且j转移优于k,则f[j]+j*(j+1)/2-ij<f[k]+k*(k+1)/2-ik,(f[j]+j*(j+1)/2-f[k]-k*(k+1)/2)/(j-k)<i。
维护下凸壳即可。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; int read() { int 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<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } #define N 1000010 #define ll long long int n,a[N],q[N]; ll f[N]; long double calc(int j,int k) { return (long double)(f[j]+(1ll*j*(j+1)>>1)-f[k]-(1ll*k*(k+1)>>1))/(j-k); } int main() { #ifndef ONLINE_JUDGE freopen("bzoj3156.in","r",stdin); freopen("bzoj3156.out","w",stdout); const char LL[]="%I64d\n"; #else const char LL[]="%lld\n"; #endif n=read(); for (int i=1;i<=n;i++) a[i]=read(); f[0]=0; int head=1,tail=1;q[1]=0; for (int i=1;i<=n;i++) { while (head<tail&&calc(q[head],q[head+1])<i) head++; f[i]=f[q[head]]+(1ll*q[head]*(q[head]+1)>>1)-1ll*i*q[head]+(1ll*i*(i-1)>>1)+a[i]; while (head<tail&&calc(q[tail-1],q[tail])>calc(q[tail],i)) tail--; q[++tail]=i; } cout<<f[n]; return 0; }