BZOJ3156: 防御准备

来源:互联网 发布:mac如何装win10 编辑:程序博客网 时间:2024/05/21 11:14

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

题目传送门

学了斜率优化之后,我DP的劣势一下被拉了回来,爽啊!
这一看是DP啊,然后列了个方程:

设f[i]表示在第i个位置放防御塔,f[i]=min(f[i],f[j]+a[i]+(i-j)*(i-j-1)/2) 

这绝对会TLE啊,n有100000啊!
这又是单调递增的,所以斜率优化
可是细节问题啊…
按照我这个方程,输入是要倒过来的,为什么?
因为他是要找右边的防御塔啊..问题1
还有你询问n后你要访问一遍n+1,这就能满足另外一种状况,他前面有防御塔…
那么第三个问题就是你第一个是一定要建的啊….要从2开始枚举!!
推斜率方程的过程我给了,但我真心希望你们自己打

代码如下:

#include<cmath>#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>using namespace std;typedef long long ll;ll f[1110000],a[1100000];/*    设f[i][0]表示在第i个位置放守卫塔,f[i][1]表示在第i个位置放木偶    j是i右边第一个放防御塔的位置...算了2维了懒得用    设f[i]表示在第i个位置放防御塔,    f[i]=min(f[i],f[j]+a[i]+(i-j)*(i-j-1)/2)     序列单调递增    设j1<j2<i     f[j2]+a[i]+(i-j2)*(i-j2-1)/2<=f[j1]+a[i]+(i-j1)*(i-j1-1)    f[j2]-f[j1] <= (i-j1)*(i-j1-1)/2-(i-j2)*(i-j2-1)/22*(f[j2]-f[j1]) <= (i-j1)*(i-j1-1)/2-(i-j2)*(i-j2-1)                <= i*i -i*j1 - i -i*j1 +j1*j1 +j1 -(i*i -i*j2 - i -i*j2 +j2*j2 +j2 )                <=  -i*j1  -i*j1 +j1*j1 +j1  +i*j2  +i*j2 -j2*j2 -j2                 <=  2*i*(j2-j1)+  j1*j1-j2*j2 + j1-j2     2*(f[j2]-f[j1])-j1*j1+j2*j2-j1+j2<=2*i*(j2-j1)    (2*(f[j2]-f[j1])-j1*j1+j2*j2-j1+j2)/(j2-j1)/2<=i*/int n;int head,tail;int list[1110000];double slope(ll j1,ll j2){    return ( 2*(f[j2]-f[j1]) -j1*j1+j2*j2-j1+j2  )/((j2-j1)*2);}int main(){    scanf("%d",&n);    for(int i=n;i>=1;i--)scanf("%lld",&a[i]);    list[1]=1;head=1;tail=1;f[1]=a[1];    for(ll i=2;i<=n+1;i++)    {        while(head<tail&&slope(list[head],list[head+1])<i)head++;        ll j=list[head];        f[i]=f[j]+a[i]+(i-j-1)*(i-j)/2;        while(head<tail&&slope(list[tail],i)<slope(list[tail],list[tail-1]))tail--;        list[++tail]=i;    }    printf("%lld\n",min(f[n],f[n+1]));    return 0;}

by_lmy