[BZOJ3156] 防御准备

题目描述

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

题目分析

令f[i]为第i个点放塔 1~i战线花费最小值
列出方程:
f[i]=f[j]+∑(i-t) (j+1≤t≤i) +c[i]
=f[j]+i*(i-j)-∑t (j+1≤t≤i) +c[i]
=f[j]+i*(i-j)-(sum[i]-sum[j]) +c[i]
移项:f[j]+sum[j]=i*j+f[i]+sum[i]-i*i-c[i]
之后斜率优化即可

#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <algorithm>
using namespace std;
long long sum[2000000],f[2000000],c[2000000];
int n,l,r;
long long q[2000000];
long long yi(long long i) { return f[i]+sum[i]; }
int main()
{
    scanf("%d",&&n);
    for(int i=1;i<=n;i++) scanf("%lld",&&c[i]);
    for(int i=1;i<=n;i++) sum[i]=sum[i-1]+i;
    for(int i=1;i<=n;i++)
    {
        while(l<r&&&&yi(q[l+1])-yi(q[l])<i*(q[l+1]-q[l])) l++;
        f[i]=f[q[l]]+i*(i-q[l])-sum[i]+sum[q[l]]+c[i];
        while(l<r&&&&(yi(i)-yi(q[r]))*(q[r]-q[r-1])<(yi(q[r])-yi(q[r-1]))*(i-q[r])) r--;
        q[++r]=i;
    }
    printf("%lld",f[n]);
}
/*f[i]=f[j]+i*(i-j)-sum[i]+sum[j]+c[i];
f[j]+sum[j]=i*j+sum[i]-i*i+f[i]-c[i];*/ 

发表评论

邮箱地址不会被公开。 必填项已用*标注