Problem
Solution
我们设 f ( i ) f(i) f(i) 为处理到第 i i i 个位置,且第 i i i 个位置放塔的最小花费,设 S ( i ) S(i) S(i) 为 i i i 的前缀和(即 S ( i ) = ∑ j = 1 i j S(i)=\sum_{j=1}^ij S(i)=∑j=1ij)。
那么根据题意,有以下的转移方程:
f ( i ) = min j = 1 i − 1 { f ( j ) + ∑ k = j + 1 i − 1 ( i − k ) + a i } f(i)=\min_{j=1}^{i-1}\{f(j)+\sum_{k=j+1}^{i-1}(i-k)+a_i\} f(i)=j=1mini−1{f(j)+k=j+1∑i−1(i−k)+ai}
再进一步化简一下,得到:
f ( i ) = min j = 1 i − 1 { f ( j ) + ( i − j − 1 ) × i − ( S ( i − 1 ) − S ( j ) ) + a i } f(i)=\min_{j=1}^{i-1}\{f(j)+(i-j-1)\times i-(S(i-1)-S(j))+a_i\} f(i)=j=1mini−1{f(j)+(i−j−1)×i−(S(i−1)−S(j))+ai}
于是我们按照套路,讨论 k < j < i k<j<i k<j<i,且 j j j 比 k k k 更优的条件,即:
f ( j ) + ( i − j − 1 ) × i − ( S ( i − 1 ) − S ( j ) ) + a i < f ( k ) + ( i − k − 1 ) × i − ( S ( i − 1 ) − S ( k ) ) + a i f(j)+(i-j-1)\times i-(S(i-1)-S(j))+a_i<f(k)+(i-k-1)\times i-(S(i-1)-S(k))+a_i f(j)+(i−j−1)×i−(S(i−1)−S(j))+ai<f(k)+(i−k−1)×i−(S(i−1)−S(k))+ai
化简得:
f ( j ) + S ( j ) − ( f ( k ) + S ( k ) ) j − k < i \frac{f(j)+S(j)-(f(k)+S(k))}{j-k}<i j−kf(j)+S(j)−(f(k)+S(k))<i
由于 i i i 是单调不减的,我们就直接单调队列维护然后转移就可以了。
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 1000005
#define ll long long
using namespace std;
ll f[N],S[N],Q[N];
double slope(int x,int y) {return (1.0*f[y]-f[x]+S[y]-S[x])/(1.0*y-x);}
int main(){
int n,i,x;
scanf("%d",&n);
int l=0,r=0;
for(i=1;i<=n;++i){
scanf("%d",&x),S[i]=S[i-1]+i;
while(l<r&&slope(Q[l],Q[l+1])<i) l++;
f[i]=f[Q[l]]+(i-Q[l]-1)*i-(S[i-1]-S[Q[l]])+x;
while(l<r&&slope(Q[r-1],Q[r])>slope(Q[r],i)) r--;
Q[++r]=i;
}
printf("%lld",f[n]);
return 0;
}