bzoj3156: 防御准备
为什么我又是一眼秒算法然后搞了这么久。。。一开始还想了个二维。。。
那么这题DP方程很好写:f[i]=min{a[i]+f[j]+(i-j)*(i-j+1)/2}
ans=min{f[i]+(n-i)*(n-i+1)/2}
明显有单调性
那么可以化出 ((2*f[j1]+j1^2)-(2*f[j2]+j2^2)) / (j1-j2) <=2*i+1
优化到O(n)
#include<cstdio> #include<iostream> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> using namespace std; typedef long long LL; LL a[1100000],f[1100000]; double slope(LL j1,LL j2) { return ( (double(2*f[j1]+j1*j1)-double(2*f[j2]+j2*j2)) )/( (double(j1))-(double(j2)) ); } LL list[1100000]; int main() { int n; scanf("%d",&n); for(int i=1;i<=n;i++)scanf("%lld",&a[n-i+1]); f[1]=a[1];LL ans=f[1]+(LL(n-1))*(LL(n-1+1))/2; int head=1,tail=1;list[1]=1; for(int i=2;i<=n;i++) { while(head<tail&&slope(list[head],list[head+1])<=(double(2*i-1)))head++; int j=list[head]; f[i]=f[j]+a[i]+(LL(i-1-j))*(LL(i-j))/2LL; ans=min(ans,f[i]+(LL(n-i))*(LL(n-i+1))/2); while(head<tail&&slope(list[tail-1],list[tail])>=slope(list[tail],i))tail--; list[++tail]=i; } printf("%lld\n",ans); return 0; }
pain and happy in the cruel world.