题意:(N(N<=10^6))个牧场,牧场i要么花费(a_i)放置控制站,要么花费它到它右边第一个控制站之间的牧场数目(不包括自身,但包括控制站所在牧场)乘上该牧场的放养量(b_i).求最小总花费.
设(f[i])表示在i点放置控制站的最小花费.
(f[i]=min(f[j]+a[i]+(sum_{k=j+1}^{i-1}b[k]*(i-k))))
设(sum[i])表示(b[i])的前缀和,(s[i])表示(i*b[i])的前缀和,则上式可以写成
(f[i]=f[j]+a[i]+i*(sum[i-1]-sum[j])-(s[i-1]-s[j]))
设(k<j)且j比k更优,即
(f[j]+a[i]+i*(sum[i-1]-sum[j])-(s[i-1]-s[j])<f[k]+a[i]+i*(sum[i-1]-sum[k])-(s[i-1]-s[k]))
整理上式得到,
(frac{f[j]+s[j]-f[k]-s[k]}{sum[j]-sum[k]}<i)
就可以斜率优化了.
#include<bits/stdc++.h>
#define LL long long
using namespace std;
inline int read(){
int s=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){s=s*10+ch-'0';ch=getchar();}
return s*w;
}
const int N=1000005;
int a[N],b[N],q[N];
LL s[N],sum[N],f[N];
inline double Count(int j,int k){return 1.0*(f[j]+s[j]-f[k]-s[k])/(double)(sum[j]-sum[k]);}
int main(){
int n=read();
for(int i=1;i<=n;i++)a[i]=read();
for(int i=1;i<=n;i++){
b[i]=read();
sum[i]=sum[i-1]+b[i];
s[i]=s[i-1]+1ll*i*b[i];
}
int l=1,r=1;
for(int i=1;i<=n;i++){
while(l<r&&Count(q[l+1],q[l])<=i)l++;
f[i]=f[q[l]]+a[i]+i*(sum[i-1]-sum[q[l]])-(s[i-1]-s[q[l]]);
while(l<r&&Count(q[r],q[r-1])>=Count(i,q[r]))r--;
q[++r]=i;
}
printf("%lld
",f[n]);
return 0;
}