[APIO2010]特别行动队

题目描述

你有一支由 $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 
输出
9

说明/提示

样例输入输出 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 \times 4 -20 = 4$,$-3^2 - 10 \times 3 - 20 = 1$,$-4^2 + 10 \times 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:

这道题应该是比较板的斜优了。
首先把$Dp$式子写起来,设$f\left [ i \right ]$表示$1$到$i$分成若干组的最大战斗力。所以:

$$f\left [ i \right ]= \max\left \{ f\left [ j \right ]+a\times \left ( sum\left [ i \right ] -sum\left [ j \right ]\right )^{2}+b
\times \left ( sum\left [ i \right ] -sum\left [ j \right ]\right ) + c \right \}$$

其中$j< i$。

把$\max$去掉。因为我们要求的是$f[i]$,所以我们把已知的有关$j$的项都移到等式左边去,含$i$的未知的项都丢到右边,所以化出来就是这样:

$$f[j]+a \times sum[j]^2-b \times sum[j]=sum[i] \times (2 \times sum[j] \times a-b-a \times sum[i])+f[i]-c$$

所以$y=f[j]+a \times sum[j]^2-b \times sum[j]$,$k=sum[i] $,$x=2 \times sum[j] \times a-b-a \times 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\n",f[n]);
24     return 0;
25 }

 

posted @ 2021-02-05 21:05  谁伴我流浪  阅读(148)  评论(0编辑  收藏  举报