【BZOJ】【3156】防御准备

时间:2021-11-21 16:32:20

DP/斜率优化


  斜率优化的裸题……

  sigh……又把$10^6$当成10W了……RE了N发

  这题还是很水的

  当然逆序也能做……不过还是整个反过来比较顺手

  反转后的a[0]=反转前的a[n],以此类推直到a[n-1],反转后的a[n]=0;

  令f[0]=a[0],因为最初状态必须选第一个守卫塔。

  然后定义f[i]表示在第 i 个位置放守卫塔,0~i 的代价最小值

  易得$f[i]=min\{ f[j]+\frac{(i-j)*(i-j-1)}{2}+a[i] \} $

  单调性证明:$( j > k ) $

\[ \begin{aligned} f[j]-f[k]+\frac{(i-j)*(i-j-1)}{2}-\frac{ (i-k)*(i-k-1)}{2} &< 0 \\ f[j]-f[k]+\frac{j^2+j-k^2-k}{2} &< i*(j-k) \\ \frac{f[j]-f[k]+\frac{j^2+j-k^2-k}{2}}{j-k} &< i \end{aligned} \]

 /**************************************************************
Problem: 3156
User: Tunix
Language: C++
Result: Accepted
Time:2476 ms
Memory:24708 kb
****************************************************************/ //BZOJ 3156
#include<cstdio>
#include<iostream>
#include<algorithm>
#define rep(i,n) for(int i=0;i<n;++i)
#define F(i,j,n) for(int i=j;i<=n;++i)
#define D(i,j,n) for(int i=j;i>=n;--i)
#define pb push_back
using namespace std;
int getint(){
int v=,sign=; char ch=getchar();
while(ch<''||ch>''){ if (ch=='-') sign=-; ch=getchar();}
while(ch>=''&&ch<=''){ v=v*+ch-''; ch=getchar();}
return v*sign;
}
const int N=;
typedef long long LL;
/******************tamplate*********************/
LL n,q[N],l,r;
LL a[N],f[N];
inline double slop(LL k,LL j){
return double(f[j]-f[k]+(j*j+j-k*k-k)/2.0)/double(j-k);
}
int main(){
n=getint();
D(i,n-,) a[i]=getint();
f[]=a[];
F(i,,n){
while(l<r && slop(q[l],q[l+])<i)l++;
int t=q[l];
f[i]=f[t]+(LL)(i-t)*(i-t-)/+a[i];
while(l<r && slop(q[r-],q[r])>slop(q[r],i)) r--;
q[++r]=i;
}
printf("%lld\n",f[n]);
return ;
}

3156: 防御准备

Time Limit: 10 Sec  Memory Limit: 512 MB
Submit: 618  Solved: 296
[Submit][Status][Discuss]

Description

 【BZOJ】【3156】防御准备

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

[Submit][Status][Discuss]