思路:设dp[i]为考虑前i个数字的最小花费。
那么dp[i] = min(dp[i],dp[j] + s[j] - s[i] - (j-i)*a[j+1])。
注意初值和状态转移的初始位置。
我的代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int maxn = 500005;
LL dp[maxn],n,m,a[maxn],s[maxn];
LL q[maxn],hd,tl;
LL getDP(LL j,LL i){
return dp[j] + (s[i]-s[j]) - (i-j)*a[j+1];
}
LL getY(LL j,LL k){
return (dp[k]-s[k]+k*a[k+1])-(dp[j]-s[j]+j*a[j+1]);
}
LL getX(LL j,LL k){
return a[k+1] - a[j+1];
}
void solve(){
hd = tl = 0;
q[tl++] = m;
for(LL i=m;i<2*m;i++) dp[i] = s[i] - i*a[1];
for(LL i=2*m;i<=n;i++){
while(hd+1<tl && getY(q[hd],q[hd+1])
<= i*getX(q[hd],q[hd+1])) hd++;
dp[i] = getDP(q[hd],i);
while(hd+1<tl && getY(q[tl-1],i-m+1)*getX(q[tl-2],q[tl-1])
<= getY(q[tl-2],q[tl-1])*getX(q[tl-1],i-m+1)) tl--;
q[tl++] = i-m+1;
}
printf("%I64d\n",dp[n]);
}
int main(){
int cas;
scanf("%d",&cas);
while(cas--){
scanf("%I64d%I64d",&n,&m);
for(LL i=1;i<=n;i++){
scanf("%I64d",&a[i]);
s[i] = s[i-1] + a[i];
}
solve();
}
return 0;
}