由朴素转移方程
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;
}