bzoj3156 防御准备(斜率优化)
Time Limit: 10 Sec Memory Limit: 512 MB
Input
第一行为一个整数N表示战线的总长度。
第二行N个整数,第i个整数表示在位置i放置守卫塔的花费Ai。
Output
共一个整数,表示最小的战线花费值。
Sample Input
10
2 3 1 5 4 5 6 3 1 2
2 3 1 5 4 5 6 3 1 2
Sample Output
18
HINT
1<=N<=10^6,1<=Ai<=10^9
设$f[i]$为已经完成$i+1$到$n$,在$i$放塔的最小代价
$f[i]=a[i]+min(f[j]+ \frac {(j-i-1)(j-i)}{2}),i<j<=n$
移项转化,得
$f[j]+\frac{j(j-1)}{2}=ij+f[i]-\frac{i(i+1)}{2}$
$k=i,x=j$单调
凸包维护单调队列,复杂度$O(n)$
#include<cstdio> typedef long long ll; inline ll min(ll A,ll B){return A<B?A:B;} inline ll max(ll A,ll B){return A>B?A:B;} int read(){ char c=getchar(); int x=0; while(c<'0'||c>'9') c=getchar(); while('0'<=c&&c<='9') x=x*10+c-48,c=getchar(); return x; } #define N 1000005 int n,a[N],h[N],L,R; ll f[N],ans=1e16; inline ll y(ll j){return f[j]+j*(j-1)/2;} inline int chk(ll A,ll B,ll kx,ll ky){return (y(A)-y(B))*kx>=ky*(A-B);} int main(){ R=n=read(); for(register int i=1;i<=n;++i) a[i]=read(); f[n]=a[n]; h[L=R=1]=n; for(register ll i=n-1;i;--i){ while(L<R&&chk(h[L],h[L+1],1,i)) ++L; f[i]=a[i]+f[h[L]]+(h[L]-i)*(h[L]-i-1)/2; while(L<R&&chk(h[R],h[R-1],h[R]-i,y(h[R])-y(i))) --R; h[++R]=i; ans=min(ans,f[i]+i*(i-1)/2); }printf("%lld",ans); return 0; }