令 (f_i) 表示前 (i) 个士兵组成特别行动队的最大修正战斗力之和。对 (x) 做一遍前缀和。
转移方程:
[f_i=max{f_j+a imes(x_i-x_j)^2+b imes(x_i-x_j)+c|j<i}
]
考虑两个决策 (j,k(j<k)),若 (j) 比 (k) 优:
[f_j+a imes(x_i-x_j)^2+b imes(x_i-x_j)+c>f_k+a imes(x_i-x_k)^2+b imes(x_i-x_k)+c
]
[f_j-f_k>a imes(x_i^2-2 imes x_i imes x_k+x_k^2-x_i^2+2 imes x_i imes x_j-x_j^2)+b imes(x_i-x_k-x_i+x_j)
]
[f_j-f_k>a imes x_k^2+2a imes x_i imes x_j-2a imes x_i imes x_k-a imes x_j^2+b imes x_j-b imes x_k
]
[f_j+a imes x_j^2-b imes x_j-f_k-a imes x_k^2+b imes x_k>x_i imes(2a imes x_j-2a imes x_k)
]
[dfrac{f_j+a imes x_j^2-b imes x_j-f_k-a imes x_k^2+b imes x_k}{2a imes x_j-2a imes x_k}>x_i
]
两个负的负负得正不用变号。因为 (x_i) 随着 (i) 的增大单调递增,所以对 ((2a imes x_i,f_i+a imes x_i^2-b imes x_i)) 维护一个下凸包。
时间复杂度 (Oleft(n ight))。
code:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define N 1000005
#define Db double
#define For(i,x,y)for(i=x;i<=(y);i++)
ll f[N];
int x[N],que[N],a,b,c;
int read()
{
int A;
bool K;
char C;
C=A=K=0;
while(C<'0'||C>'9')K|=C=='-',C=getchar();
while(C>'/'&&C<':')A=(A<<3)+(A<<1)+(C^48),C=getchar();
return(K?-A:A);
}
inline ll sqr(ll p)
{
return p*p;
}
inline Db slope(int j,int k)
{
return Db(f[j]+a*sqr(x[j])-1LL*b*x[j]-f[k]-a*sqr(x[k])+1LL*b*x[k])/Db((a<<1)*(x[j]-x[k]));
}
int main()
{
int n,i,head,tail;
n=read(),a=read();
b=read(),c=read();
head=tail=1;
For(i,1,n)
{
x[i]=x[i-1]+read();
while(head<tail&&slope(que[head],que[head+1])<=x[i])head++;
f[i]=f[que[head]]+1LL*a*sqr(x[i]-x[que[head]])+1LL*b*(x[i]-x[que[head]])+c;
while(head<tail&&slope(que[tail-1],i)>=slope(que[tail],i))tail--;
que[++tail]=i;
}
cout<<f[n];
return 0;
}