description
你有一支由n名预备役士兵组成的部队,士兵从1到n编号,要将他们拆分成若干特别行动队调入战场。出于默契的考虑,同一支特别行动队中队员的编号应该连续,即为形如(i, i + 1, …, i + k)的序列。 编号为i的士兵的初始战斗力为xi ,一支特别行动队的初始战斗力x为队内士兵初始战斗力之和,即x = xi + xi+1 + … + xi+k。 通过长期的观察,你总结出一支特别行动队的初始战斗力x将按如下经验公式修正为x’:x’ = ax2 + bx + c,其中a, b, c是已知的系数(a < 0)。 作为部队统帅,现在你要为这支部队进行编队,使得所有特别行动队修正后战斗力之和最大。试求出这个最大和。 例如,你有4名士兵,x1 = 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,没有其它方案能使修正后的战斗力和更大。
analysis
-
达成成就一天两斜率优化 -
设 f [ i ] f[i] f[i]表示到第 i i i位的最小值,那么 O ( n 2 ) O(n^2) O(n2)的 D P DP DP方程很好写
f [ i ] = m i n ( f [ j ] + a ( p r e [ i ] − p r e [ j ] ) 2 + b ( p r e [ i ] − p r e [ j ] ) + c ) f[i]=min(f[j]+a(pre[i]-pre[j])^2+b(pre[i]-pre[j])+c) f[i]=min(f[j]+a(pre[i]−pre[j])2+b(pre[i]−pre[j])+c)
- 对于 k > j k>j k>j且 k k k比 j j j优则有
f [ k ] + a ( p r e [ i ] − p r e [ k ] ) 2 + b ( p r e [ i ] − p r e [ k ] ) + c ≥ f [ j ] + a ( p r e [ i ] − p r e [ j ] ) 2 + b ( p r e [ i ] − p r e [ j ] ) + c f[k]+a(pre[i]-pre[k])^2+b(pre[i]-pre[k])+c≥f[j]+a(pre[i]-pre[j])^2+b(pre[i]-pre[j])+c f[k]+a(pre[i]−pre[k])2+b(pre[i]−pre[k])+c≥f[j]+a(pre[i]−pre[j])2+b(pre[i]−pre[j])+c
- 拆项移项得
f [ k ] − f [ j ] ≥ a ( p r e [ i ] − p r e [ j ] ) 2 − a ( p r e [ i ] − p r e [ k ] ) 2 + b ( p r e [ i ] − p r e [ j ] ) − b ( p r e [ i ] − p r e [ k ] ) f[k]-f[j]≥a(pre[i]-pre[j])^2-a(pre[i]-pre[k])^2+b(pre[i]-pre[j])-b(pre[i]-pre[k]) f[k]−f[j]≥a(pre[i]−pre[j])2−a(pre[i]−pre[k])2+b(pre[i]−pre[j])−b(pre[i]−pre[k])
- 再移
f [ k ] − f [ j ] ≥ a ( p r e [ i ] 2 − 2 p r e [ i ] p r e [ j ] + p r e [ j ] 2 ) − a ( p r e [ i ] 2 − 2 p r e [ i ] p r e [ j ] + p r e [ k ] 2 ) + b ( p r e [ k ] − p r e [ j ] ) f[k]-f[j]≥a(pre[i]^2-2pre[i]pre[j]+pre[j]^2)-a(pre[i]^2-2pre[i]pre[j]+pre[k]^2)+b(pre[k]-pre[j]) f[k]−f[j]≥a(pre[i]2−2pre[i]pre[j]+pre[j]2)−a(pre[i]2−2pre[i]pre[j]+pre[k]2)+b(pre[k]−pre[j])
f [ k ] + a p r e [ k ] 2 − f [ j ] − a p r e [ j ] 2 − b ( p r e [ k ] − p r e [ j ] ) ≥ a p r e [ i ] ( 2 p r e [ k ] − 2 p r e [ j ] ) f[k]+apre[k]^2-f[j]-apre[j]^2-b(pre[k]-pre[j])≥apre[i](2pre[k]-2pre[j]) f[k]+apre[k]2−f[j]−apre[j]2−b(pre[k]−pre[j])≥apre[i](2pre[k]−2pre[j])
- 最后
f [ k ] + a p r e [ k ] 2 − f [ j ] − a p r e [ j ] 2 − b ( p r e [ k ] − p r e [ j ] ) 2 a ( p r e [ k ] − p r e [ j ] ) ≥ p r e [ i ] {{f[k]+apre[k]^2-f[j]-apre[j]^2-b(pre[k]-pre[j])}\over{2a(pre[k]-pre[j])}}≥pre[i] 2a(pre[k]−pre[j])f[k]+apre[k]2−f[j]−apre[j]2−b(pre[k]−pre[j])≥pre[i]
-
然后按套路上单调队列斜率优化维护上凸壳就可以了
-
注意先删去较劣的队头后删去较劣的队尾
-
还有这题删劣决策点的符号很奇怪,我也不太懂
-
你听说什么是如出一辙吗,推荐试下找不同
code
#pragma GCC optimize("O3")
#pragma G++ optimize("O3")
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define MAXN 1000005
#define mod 1000000007
#define ll long long
#define reg register ll
#define fo(i,a,b) for (reg i=a;i<=b;++i)
#define fd(i,a,b) for (reg i=a;i>=b;--i)
#define O3 __attribute__((optimize("-O3")))
using namespace std;
ll a[MAXN],f[MAXN],pre[MAXN],que[MAXN];
ll n,A,B,C;
O3 inline ll read()
{
ll x=0,f=1;char ch=getchar();
while (ch<'0' || '9'<ch){if (ch=='-')f=-1;ch=getchar();}
while ('0'<=ch && ch<='9')x=x*10+ch-'0',ch=getchar();
return x*f;
}
O3 inline ll sqr(ll x)
{
return x*x;
}
O3 inline double slope(ll x,ll y)
{
return 1.0*(f[x]+A*sqr(pre[x])-f[y]-A*sqr(pre[y])-B*(pre[x]-pre[y]))/(2.0*A*(pre[x]-pre[y]));
}
O3 int main()
{
//freopen("T3.in","r",stdin);
n=read(),A=read(),B=read(),C=read();
fo(i,1,n)pre[i]=pre[i-1]+(a[i]=read());
ll head=1,tail=1;f[0]=que[1]=0;
fo(i,1,n)
{
while (head<tail && slope(que[head],que[head+1])<=pre[i])++head;
f[i]=f[que[head]]+A*sqr(pre[i]-pre[que[head]])+B*(pre[i]-pre[que[head]])+C;
while (head<tail && slope(que[tail-1],que[tail])>=slope(que[tail],i))--tail;
que[++tail]=i;
}
printf("%lld\n",f[n]);
return 0;
}