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;
}

 

posted @ 2018-04-26 07:40  AKCqhzdy  阅读(109)  评论(0编辑  收藏  举报