题目描述
你有一支由 $n$ 名预备役士兵组成的部队,士兵从 $1$ 到 $n$ 编号,你要将他们拆分成若干特别行动队调入战场。出于默契的考虑,同一支特别行动队中队员的编号应该连续,即为形如 $(i, i + 1, cdots i + k)$的序列。所有的队员都应该属于且仅属于一支特别行动队。
编号为 $i$的士兵的初始战斗力为 $x_i$,一支特别行动队的初始战斗力 $X$ 为队内士兵初始战斗力之和,即 $X = x_i + x_{i+1} + cdots + x_{i+k}$。
通过长期的观察,你总结出对于一支初始战斗力为 $X$ 的特别行动队,其修正战斗力 $X'= aX^2+bX+c$,其中 $a,~b,~c$ 是已知的系数$(a < 0)$。 作为部队统帅,现在你要为这支部队进行编队,使得所有特别行动队的修正战斗力之和最大。试求出这个最大和。
输入格式
输入的第一行是一个整数 $n$,代表士兵的人数。
输入的第二行有三个用空格隔开的整数,依次代表 $a,~b,~c$,即修正战斗力的系数。
输入的第三行有$n$个用空格隔开的整数,第 $i$ 个整数代表编号为 $i$ 的士兵的初始战斗力$x_i$。
输出格式
输出一行一个整数,代表最大的所有特别行动队战斗力之和。
输入输出样例
4 -1 10 -20 2 2 3 4
说明/提示
样例输入输出 1 解释
你有 $4$ 名士兵,$x_1 = 2,~x_2 = 2,~x_3 = 3,~x_4=4$。修正战斗力公式中的参数为 $a = -1,~b = 10,~c = -20$。
此时,最佳方案是将士兵组成 $3$ 个特别行动队:第一队包含士兵 $1$ 和士兵 $2$,第二队包含士兵 $3$,第三队包含士兵 $4$。特别行动队的初始战斗力分别为 $4,~3,~4$,修正后的战斗力分别为 $-4^2 + 10 imes 4 -20 = 4$,$-3^2 - 10 imes 3 - 20 = 1$,$-4^2 + 10 imes 4 -20 = 4$。修正后的战斗力和为 $4 + 1 + 4 = 9$,没有其它方案能使修正后的战斗力和更大。
数据范围与约定
对于 $20\%$ 的数据,$n leq 10^3$。
对于 $50\%$的数据,$n leq 10^4$
对于 $100\%$的数据,$1 leq n leq 10^6$,$-5 leq a leq -1$,$-10^7 leq b leq 10^7$,$-10^7 leq c leq 10^7$,$1 leq x_i leq 100$。
Solution:
$$fleft [ i
ight ]= maxleft { fleft [ j
ight ]+a imes left ( sumleft [ i
ight ] -sumleft [ j
ight ]
ight )^{2}+b
imes left ( sumleft [ i
ight ] -sumleft [ j
ight ]
ight ) + c
ight }$$
其中$j< i$。
把$max$去掉。因为我们要求的是$f[i]$,所以我们把已知的有关$j$的项都移到等式左边去,含$i$的未知的项都丢到右边,所以化出来就是这样:
$$f[j]+a imes sum[j]^2-b imes sum[j]=sum[i] imes (2 imes sum[j] imes a-b-a imes sum[i])+f[i]-c$$
所以$y=f[j]+a imes sum[j]^2-b imes sum[j]$,$k=sum[i] $,$x=2 imes sum[j] imes a-b-a imes sum[i]$,$b=f[i]-c$。
因为我们之前求的$f[j]$是最大的,而这条直线的斜率是一定的,所以当$y$最大时,截距也最大,因为$c$是一定的,所以此时$f[i]$也最大。
因为$sum[i]$是单调递增的,所以斜率是单调递增的,所以直接用单调队列维护即可。
$Code:$
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=1e6+10; 4 long long n,sum[N],x[N],l=1,r=1,q[N]; 5 long long f[N],a,b,c; 6 long double slop(long long i,long long j){ 7 return (long double)(f[j]+a*sum[j]*sum[j]-b*sum[j]-f[i]-a*sum[i]*sum[i]+b*sum[i])/(long double)(2*a*sum[j]-2*a*sum[i]); 8 } 9 int main(){ 10 scanf("%lld",&n); 11 scanf("%lld%lld%lld",&a,&b,&c); 12 for(long long i=1;i<=n;i++){ 13 scanf("%lld",&x[i]); 14 sum[i]=sum[i-1]+x[i]; 15 } 16 q[1]=0; 17 for(long long i=1;i<=n;i++){ 18 while(l<r&&slop(q[l],q[l+1])<=sum[i]) l++; 19 f[i]=f[q[l]]+a*(sum[i]-sum[q[l]])*(sum[i]-sum[q[l]])+b*(sum[i]-sum[q[l]])+c; 20 while(l<r&&slop(q[r-1],q[r])>=slop(q[r],i)) r--; 21 q[++r]=i; 22 } 23 printf("%lld ",f[n]); 24 return 0; 25 }