Description
我们可以从n个数中选择一些,选择第i个数的代价为Ci,且必须选择n。对于每个没有被选择的数i,若它右边离它最近的一个被选择的数是j,则代价为j-i。
求最小代价。
n<=10^6
Solution
N^2Dp还是很显然的。
设Fi表示i必须选,且i的右边已经搞定了的最小代价,那么
Fi=min(Fk+(k−i−1)∗(k−i)/2)+Ci
然后就可以考虑斜率优化了。
把括号拆开,移项,得到决策j比k优的不等式:
Fj−Fk+(j2−k2+k−j)/2j−k<i
然后就普通的打发了。
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define N 1000005
using namespace std;
typedef long long ll;
typedef double db;
ll f[N],c[N],ans;
int n,d[N],l,r;
ll calc(int x,int y) {
return (ll)y*(y-x)-(ll)(y-x)*(x+y-1)/2;
}
db g(ll x,ll y) {
return (f[x]-f[y]+(x*x-y*y-x+y)/2)*1.0/(x-y);
}
int main() {
scanf("%d",&n);
fo(i,1,n) scanf("%lld",&c[i]);
f[n]=c[n];ans=f[n]+calc(1,n);d[l=r=1]=n;
fd(i,n-1,1) {
while (l<r&&g(d[l],d[l+1])>=i) l++;
f[i]=f[d[l]]+calc(i+1,d[l])+c[i];
ans=min(ans,f[i]+calc(1,i));
while (l<r&&g(d[r-1],d[r])<g(d[r],i)) r--;
d[++r]=i;
}
printf("%lld",ans);
}