Description
Input
第一行为一个整数N表示战线的总长度。
第二行N个整数,第i个整数表示在位置i放置守卫塔的花费Ai。
Output
共一个整数,表示最小的战线花费值。
Sample Input
10
2 3 1 5 4 5 6 3 1 2
Sample Output
18
HINT
1<=N<=10^6,1<=Ai<=10^9
把a[i]反过来。
f[i]表示在第i个点修防御塔的最小值
f[i]=min(f[j]+(i-j)*(i-j-1)/2)+a[i]
推一下方程
2*(f[j]-f[k])<(i-k)*(i-k-1)-(i-j)(i-j-1)
(2*(f[j]-f[k])+j(j+1)-k(k+1)/2*(j-k)<i
维护一个凸包。
#include<cstdio>
#include<cstring>
#include<cstdlib>
#define ll long long
const int N=1100000;
ll f[N];
ll sa[N],sb[N];
int yu[N];
int n;
double slope(int k,int j)
{
return ((double)2*(double)(f[j]-f[k])+(double)j*(double)(j+1)-(double)k*(double)(k+1))/double((double)2*(double)(j-k));
}
ll min(ll x,ll y)
{
if(x<y) return x;
return y;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%lld",&sa[i]);
int len=0;
for(int i=n;i>=1;i--)
sb[++len]=sa[i];
ll ans=sb[1]+(ll)n*(ll)(n-1)/(ll)2;
int l=1,r=1;yu[1]=1;
f[1]=sb[1];
for(int i=2;i<=n;i++)
{
while(l<r&&slope(yu[l],yu[l+1])<(double)i) l++;
int k2=yu[l];
f[i]=f[k2]+(ll)(i-k2)*(ll)(i-k2-1)/2+sb[i];
ans=min(ans,f[i]+(ll)(n-i)*(ll)(n+1-i)/(ll)2);
while(l<r&&slope(yu[r-1],yu[r])>slope(yu[r],i)) r--;
yu[++r]=i;
}
printf("%lld\n",ans);
}