传送门
题解:
设 f [ i ] f[i] f[i]表示在 i i i处放置最左端的守卫塔时候,考虑所有 i i i以及 i i i右边的情况的最小花费。然后在 i i i左边全部放上木偶,则答案为 min 1 ≤ i ≤ n ( f [ i ] + ( i − 1 ) ∗ i / 2 ) \min\limits_{1\leq i \leq n}(f[i]+(i-1)*i/2) 1≤i≤nmin(f[i]+(i−1)∗i/2)
对于 f [ i ] f[i] f[i]我们有状态转移方程:
f [ i ] = min j = i + 1 n ( f j + ( j − i ) ( j − i + 1 ) / 2 ) + a i f[i]=\min_{j=i+1}^n(f_j+(j-i)(j-i+1)/2)+a_i f[i]=j=i+1minn(fj+(j−i)(j−i+1)/2)+ai
转化得到: f [ i ] = min j > i ( f j + ( j − 1 ) j / 2 − i j ) + ( i + 1 ) i / 2 + a [ i ] f[i]=\min_{j>i}(f_j+(j-1)j/2-ij)+(i+1)i/2+a[i] f[i]=j>imin(fj+(j−1)j/2−ij)+(i+1)i/2+a[i]
以 ( − j , f [ j ] + ( j − 1 ) ∗ j / 2 ) (-j,f[j]+(j-1)*j/2) (−j,f[j]+(j−1)∗j/2)为点建立下凸壳,以 i i i为斜率询问即可。
代码:
#include<bits/stdc++.h>
#define ll long long
#define re register
#define gc get_char
#define cs const
namespace IO{
inline char get_char(){
static cs int Rlen=1<<22|1;
static char buf[Rlen],*p1,*p2;
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}
template<typename T>
inline T get(){
char c;
while(!isdigit(c=gc()));T num=c^48;
while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
return num;
}
inline int getint(){return get<int>();}
}
using namespace IO;
using std::cerr;
using std::cout;
cs int N=1e6+6;
int n;
struct Point{
ll x,y;
Point(){}
Point(cs ll &_x,cs ll &_y):x(_x),y(_y){}
friend Point operator+(cs Point &a,cs Point &b){return Point(a.x+b.x,a.y+b.y);}
friend Point operator-(cs Point &a,cs Point &b){return Point(a.x-b.x,a.y-b.y);}
friend ll operator*(cs Point &a,cs Point &b){return a.x*b.y-b.x*a.y;}
};
inline ll calc(cs Point &p,cs ll &k){
return p.x*k+p.y;
}
Point p[N];int siz,now=1;
inline void push(cs Point q){
while(siz>=1+now&&(p[siz]-q)*(p[siz-1]-q)>=0)--siz;
p[++siz]=q;
}
inline ll find_min(ll k){
while(now<siz&&calc(p[now+1],k)<=calc(p[now],k))++now;
return calc(p[now],k);
}
int a[N];
ll f[N],ans=1e18;
signed main(){
//freopen("defend.in","r",stdin);
n=getint();
for(int re i=1;i<=n;++i)a[i]=getint();f[n]=a[n];
push(Point(-n,f[n]+(ll)(n-1)*n/2));ans=f[n]+(ll)n*(n-1)/2;
for(int re i=n-1;i;--i){
f[i]=find_min(i)+(ll)(i+1)*i/2+a[i];
ans=std::min(ans,f[i]+(ll)(i-1)*i/2);
push(Point(-i,f[i]+(ll)(i-1)*i/2));
}
cout<<ans<<"\n";
return 0;
}