bzoj 3156 防御准备

防御准备

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

Source 

暴力就不多说了
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
using namespace std;
#define ll long long
ll a[2000000];
ll f[2000000][3];
int main(){
  int n;
  scanf("%d",&n);
  for (int i=1;i<=n;++i) scanf("%lld",&a[i]);
  f[n][1]=a[n];
  f[n][0]=1LL<<60;
  for (int i=n-1;i>=1;--i){
    f[i][1]=a[i]+min(f[i+1][0],f[i+1][1]);
    f[i][0]=1LL<<60;
    for (int j=n;j>i;--j)
      f[i][0]=min(f[i][0],f[j][1]+(1+j-i)*(j-i)/2);
    //cout<<i<<" "<<f[i][0]<<" "<<f[i][1]<<endl;
  }
  printf("%lld\n",min(f[1][0],f[1][1]));
  return 0;
}

斜率优化 
    设i<j<k
       f[j][1]+(1+j-i)*(j-i)/2 < f[k][1]+(1+k-i)*(k-i)/2
          i*2*(k-j)<2f[k][1]+k+k*k-2f[j][1]-j-j*j
   令 check(j,k)=2f[k][1]+k+k*k-2f[j][1]-j-j*j / 2*(k-j)
    所以   check(j,k)>i 时 (j比k优) 可以舍弃 k 
   ps:这里是倒序的!!!!!!
   check(q[r],q[r-1])<check(i,q[r])
   i<check(q[r],q[r-1])<check(i,q[r]) 时 i 最优
   check(q[r],q[r-1])<i<check(i,q[r]) 时 i 或 q[r-1] 最优
   check(q[r],q[r-1])<check(i,q[r])<i 时 q[r-1]最优
  综上舍弃q[r]
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
using namespace std;
#define ll long long
ll a[2000000];
ll f[2000000][3];
ll q[2000000];

inline double check(ll j,ll k){
  return (f[k][1]*2+k+k*k-f[j][1]*2-j-j*j)/2/(k-j);
}

int main(){
  int n;
  scanf("%d",&n);
  for (int i=1;i<=n;++i) scanf("%lld",&a[i]);
  int l=1,r=1;
  q[1]=n;
  f[n][1]=a[n];
  f[n][0]=1LL<<60;
  for (int i=n-1;i>=1;--i){
    f[i][1]=a[i]+min(f[i+1][0],f[i+1][1]);

    while (l<r&&check(q[l+1],q[l])>i) ++l;

    f[i][0]=f[q[l]][1]+(q[l]+1-i)*(q[l]-i)/2;

    while (l<r&&check(q[r],q[r-1])>check(i,q[r])) --r;
    ++r;
    q[r]=i;    
    //cout<<i<<" "<<f[i][0]<<" "<<f[i][1]<<endl;
  }
  printf("%lld\n",min(f[1][0],f[1][1]));
  return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值