BZOJ 3156: 防御准备

传送门

这一题看一眼就是 $dp$,发现限制是在右边,不妨把数列反过来,这样限制在左边比较舒服

然后显然地设 $f[i]$ 表示位置 $i$ 强制放守卫时控制 $[1,i]$ 的最小费用

那么转移直接枚举上一个守卫 $j$,因为之间放置木偶的花费为 $1+2+...+(i-j-1)=(i-j-1)(i-j)/2$,有转移:

$f[i]=f[j]+a[i]+(i-j-1)(i-j)/2$

发现后面这个是个二次函数,考虑斜率优化

把式子拆开,$f[i]=f[j]+a[i]+(i^2-i)/2+(j^2+j)/2-ij$,移项得 $f[j]+(j^2+j)/2=ij+f[i]-a[i]-(i^2-i)/2$

那么 $y=f[j]+(j^2+j),k=i,x=j,b=f[i]-a[i]-(i^2-i)/2$,因为 $k,x$ 都单调,直接斜率优化即可

最后答案是 $f[n+1]$ ,因为 $f[i]$ 表示 $i$ 强制放守卫

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
    while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
    return x*f;
}
const int N=1e6+7;
int n,a[N];
ll f[N];
int Q[N],L,R;
inline int X(int j) { return j; }
inline int Y(int j) { return f[j]+(1ll*j*j+j)/2; }
inline ll calc(int i,int j) { return f[j]+a[i]+1ll*(i-j-1)*(i-j)/2; }
inline long double slope(int i,int j) { return (long double)(Y(i)-Y(j))/(X(i)-X(j)); }
int main()
{
    n=read(); for(int i=1;i<=n;i++) a[i]=read();
    reverse(a+1,a+n+1); L=R=1; Q[L]=1; f[1]=a[1];
    for(int i=2;i<=n+1;i++)
    {
        while(L<R && calc(i,Q[L])>=calc(i,Q[L+1])) L++;
        f[i]=calc(i,Q[L]);
        while(L<R && slope(Q[R-1],i)<=slope(Q[R-1],Q[R])) R--;
        Q[++R]=i;
    }
    printf("%lld\n",f[n+1]);
    return 0;
}

 

posted @ 2019-09-11 14:13  LLTYYC  阅读(187)  评论(0编辑  收藏  举报