[luogu P3628] [APIO2010]特别行动队

[luogu P3628] [APIO2010]特别行动队

题目描述

你有一支由 n 名预备役士兵组成的部队,士兵从 1 到 n 编号,要将他们拆分 成若干特别行动队调入战场。出于默契的考虑,同一支特别行动队中队员的编号 应该连续,即为形如(i, i + 1, ..., i + k)(i,i+1,...,i+k)的序列。 编号为 i 的士兵的初始战斗力为 xi ,一支特别行动队的初始战斗力 x 为队内 士兵初始战斗力之和,即 x = x_i + x_{i+1} + ... + x_{i+k}x=xi​​+xi+1​​+...+xi+k​​。

通过长期的观察,你总结出一支特别行动队的初始战斗力 x 将按如下经验公 式修正为 $x':x' = ax2 + bx + c$,其中 a, b, c 是已知的系数(a < 0)。 作为部队统帅,现在你要为这支部队进行编队,使得所有特别行动队修正后 战斗力之和最大。试求出这个最大和。

例如,你有 4 名士兵, x_1 = 2, x_2 = 2, x_3 = 3, x_4 = 4x1​​=2,x2​​=2,x3​​=3,x4​​=4。经验公式中的参数为 a = –1, b = 10, c = –20。此时,最佳方案是将士兵组成 3 个特别行动队:第一队包含士兵 1 和士兵 2,第二队包含士兵 3,第三队包含士兵 4。特别行动队的初始战斗力分 别为 4, 3, 4,修正后的战斗力分别为 4, 1, 4。修正后的战斗力和为 9,没有其它 方案能使修正后的战斗力和更大。

输入输出格式

输入格式:

输入由三行组成。第一行包含一个整数 n,表示士兵的总数。第二行包含三 个整数 a, b, c,经验公式中各项的系数。第三行包含 n 个用空格分隔的整数 $x_1, x_2, …, x_n$,分别表示编号为 1, 2, …, n 的士兵的初始战斗力。

输出格式:

输出一个整数,表示所有特别行动队修正后战斗力之和的最大值。 

输入输出样例

输入样例#1:
4 
-1 10 -20 
2 2 3 4 
输出样例#1:
9

说明

20%的数据中,n ≤ 1000;

50%的数据中,n ≤ 10,000;

100%的数据中,1 ≤ n ≤ 1,000,000,–5 ≤ a ≤ –1,|b| ≤ 10,000,000,|c| ≤ 10,000,000,1 ≤ xi ≤ 100。

 

斜率DP pro 2。相比上一题,本蒟蒻感觉这题水多了,只是调了一下才A。

设f[i]为前i个人的战斗力最大值,s[i]为前i个人战斗力的和。易得:

f[i]=max{f[j]+a(s[i]-s[j])^2+b(s[i]-s[j])+c}(j<i)

=max{f[j]+as[i]^2-2as[i]s[j]+as[j]^2+bs[i]-bs[j]+c}

设X[i]=as[i]^2,Y[i]=bs[i],则原式

=max{f[j]+X[i]-2as[i]s[j]+X[j]+Y[i]-Y[j]+c}

设P[i]=X[i]+Y[i],Q[i]=X[i]-Y[i],则原式

=max{f[j]+P[i]+Q[i]-2as[i]s[j]+c}

则f[i]=?f[j]+P[i]+Q[i]-2as[i]s[j]+c

则在这个式子里,y=f[j]+Q[j],k=2as[i],x=s[j],b=f[i]-P[i]-c,且y=kx+b。

由于是取max,所以是维护一个上凸包,所以维护一个斜率只降不升的单调队列就可以了。

其中每个点的坐标为(xi,yi)=(s[i],f[i]+Q[i])(可以从最后那个x和y的表达式看出来)。

code:

 1 %:pragma GCC optimize(2)
 2 #include<bits/stdc++.h>
 3 #define sqr(x) ((x)*(x))
 4 #define LL long long
 5 using namespace std;
 6 const int N=1000005;
 7 const double inf=1e18;
 8 int n,l,r; LL A,B,C,ratio,s[N],X[N],Y[N],P[N],Q[N],f[N];
 9 struct point {
10     LL x,y;
11     point() {}
12     point(LL _x,LL _y):x(_x),y(_y) {}
13 }st[N];
14 inline int read() {
15     int x=0,f=1; char ch=getchar();
16     while (ch<'0'||ch>'9') f=(ch=='-')?-1:1,ch=getchar();
17     while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
18     return x*f;
19 }
20 double slope(point u,point v) {
21     return u.x==v.x?(u.y<v.y?inf:-inf):1.0*(v.y-u.y)/(v.x-u.x);
22 }
23 LL get(LL k) {
24     while (l<r&&slope(st[l],st[l+1])>1.0*k) l++;
25     return st[l].y-k*st[l].x;
26 }
27 void insert(point cur) {
28     while (l<r&&slope(st[r-1],st[r])<slope(st[r-1],cur)) r--;
29     st[++r]=cur;
30 }
31 int main() {
32     n=read(),A=read(),B=read(),C=read(),ratio=A*2,s[0]=0;
33     for (int i=1; i<=n; i++) s[i]=s[i-1]+read();
34     for (int i=1; i<=n; i++) X[i]=A*sqr(s[i]);
35     for (int i=1; i<=n; i++) Y[i]=B*s[i];
36     for (int i=1; i<=n; i++) P[i]=X[i]+Y[i];
37     for (int i=1; i<=n; i++) Q[i]=X[i]-Y[i];
38     l=1,r=0,st[++r]=point(0,0);
39     for (int i=1; i<=n; i++) {
40         f[i]=get(ratio*s[i])+P[i]+C;
41         insert(point(s[i],f[i]+Q[i]));
42     }
43     printf("%lld\n",f[n]);
44     return 0;
45 }
View Code

 

转载于:https://www.cnblogs.com/whc200305/p/7656169.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值