由朴素转移方程f[i]=Min{f[j]+(X−L)2}
其中X=sum[i]−sum[j]−i−j−1
时间复杂度O(N2)
考虑化成f[i]=A[i]∗B[i]+C[i]+D[i]的形式
设dp[i]=sum[i]+i,C=l+1
那么原式就可以化成f[i]=Min{f[j]+(dp[i]−dp[j]−C)2}
假设存在两个决策点j,k并且满足k比
那么可以得到f[k]+(dp[i]−dp[k]−C)2≤f[j]+(dp[i]−dp[j]−C)2
经化简得
dp[i]>(f[j]−f[k])+(dp[j]+C)2+(dp[k]+C)22∗(dp[j]−dp[k])
设
slope(j,k)=(f[j]−f[k])+(dp[j]+C)2+(dp[k]+C)22∗(dp[j]−dp[k])
即满足上述条件的情况下,k比
考虑用单调队列,如果slope(head,head+1)<dp[i]表示head+1比head更优,弹队首
同样,如果slope(tail−1,tail)>slope(tail,i)的话,表示i比
决策O(1),时间复杂度O(N)
#include <cstdio>
using namespace std;
typedef long long LL;
typedef double dl;
const int SN = 50000 + 10;
LL sum[SN], c[SN], l, n, f[SN], dp[SN];
int que[SN];
int head, tail;
void Read(LL &x) {
LL in = 0,f = 1;char ch = getchar();
while(ch<'0' || ch>'9') {if(ch=='-') f = -1;ch = getchar();}
while(ch>='0' && ch<='9') {in = in*10+ch-'0';ch = getchar();}
x = in*f;
}
dl get_slope(int i,int j) {
return (dl)(f[i]-f[j]+(sum[i]+l)*(sum[i]+l)-(sum[j]+l)*(sum[j]+l))/(2.0*(sum[i]-sum[j]));
}
int main() {
Read(n);Read(l);
l += 1;
for(int i = 1; i <= n; i++) {
Read(sum[i]);
sum[i] += sum[i-1];
}
for(int i = 1; i <= n; i++) sum[i] += i;
f[0] = 0;
head = tail = 1;
for(int i = 1; i <= n; i++) {
while(head < tail && get_slope(que[head],que[head+1]) <= sum[i]) head++;
f[i] = f[que[head]] + (sum[i]-sum[que[head]]-l)*(sum[i]-sum[que[head]]-l);
while(head < tail && get_slope(que[tail],i) < get_slope(que[tail-1],que[tail])) tail--;
que[++tail] = i;
}
printf("%lld\n",f[n]);
return 0;
}